/**
 * CC3Node.m
 *
 * cocos3d 0.6.1
 * Author: Bill Hollings
 * Copyright (c) 2010-2011 The Brenwill Workshop Ltd. All rights reserved.
 * http://www.brenwill.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * http://en.wikipedia.org/wiki/MIT_License
 * 
 * See header file CC3Node.h for full API documentation.
 */

#include "CC3Node.h"
#include "CC3World.h"
#include "CC3MeshNode.h"
#include "CC3BoundingVolumes.h"
#include "CC3NodeAnimation.h"
#include "CC3Billboard.h"
#include "CC3OpenGLES11Foundation.h"
#include "CC3OpenGLES11Engine.h"
#include "CCActionManager.h"
#include "CCLabelTTF.h"
#include "CCPointExtension.h"
#include "CC3TargettingNode.h"
#include <float.h>
#include "CC3OpenGLES11Matrices.h"
#include "ccMacros.h"


#pragma mark CC3Node

CC3Node::CC3Node() :
	children(NULL),
	parent(NULL),
	transformMatrix(NULL),
	transformMatrixInverted(NULL),
	globalRotationMatrix(NULL),
	rotator(NULL),
	boundingVolume(NULL),
	animation(NULL)
{
}

CC3Node::~CC3Node()
{
	if (children) 
	{
		children->release();
		children = NULL;
	}
	parent = NULL;						// not retained
	if (transformMatrix) 
	{
		transformMatrix->release();
		transformMatrix = NULL;
	}
	if (transformMatrixInverted) 
	{
		transformMatrixInverted->release();
		transformMatrixInverted = NULL;
	}
	if (globalRotationMatrix) 
	{
		globalRotationMatrix->release();
		globalRotationMatrix = NULL;
	}
	if (rotator) 
	{
		rotator->release();
		rotator = NULL;
	}
	if (boundingVolume) 
	{
		boundingVolume->release();
		boundingVolume = NULL;
	}
	if (animation) 
	{
		animation->release();
		animation = NULL;
	}
}

void CC3Node::setLocation(CC3Vector aLocation)
{
	location = aLocation;
	markTransformDirty();
}

CC3Vector CC3Node::getLocation()
{
	return location;
}

void CC3Node::setGlobalLocation(CC3Vector aGlobalLocation)
{
	globalLocation = aGlobalLocation;
}

CC3Vector CC3Node::getGlobalLocation()
{
	return globalLocation;
}

void CC3Node::setRotation(CC3Vector aRotation)
{
	rotator->setRotation(aRotation);
	markTransformDirty();
}

CC3Vector CC3Node::getRotation()
{
	return rotator->getRotation();
}

CC3Vector CC3Node::getGlobalRotation()
{
	return globalRotationMatrix->extractRotation();
}

void CC3Node::setQuaternion(CC3Vector4 aQuaternion)
{
	rotator->setQuaternion(aQuaternion);
	markTransformDirty();
}

CC3Vector4 CC3Node::getQuaternion()
{
	return rotator->getQuaternion();
}

void CC3Node::setRotationAxis(CC3Vector aAxis)
{
	rotator->setRotationAxis(aAxis);
	markTransformDirty();
}

CC3Vector CC3Node::getRotationAxis()
{
	return rotator->getRotationAxis();
}

void CC3Node::setRotationAngle(GLfloat fRotation)
{
	rotator->setRotationAngle(fRotation);
	markTransformDirty();
}

GLfloat CC3Node::getRotationAngle()
{
	return rotator->getRotationAngle();
}

void CC3Node::setScale(CC3Vector aScale)
{
	scale = aScale;
	markTransformDirty();
}

CC3Vector CC3Node::getScale()
{
	return scale;
}

CC3Vector CC3Node::getGlobalScale()
{
	return globalScale;
}

void CC3Node::setGlobalScale(CC3Vector newValue)
{
	globalScale = newValue;
}

void CC3Node::setUniformScale(GLfloat aValue)
{
	setScale(cc3v(aValue, aValue, aValue));
}

GLfloat CC3Node::getUniformScale()
{
	if (getIsUniformlyScaledLocally()) 
	{
		return scale.x;
	} 
	else 
	{
		return CC3VectorLength(scale) / kCC3VectorUnitCubeLength;
	}
}

bool CC3Node::getIsUniformlyScaledLocally()
{
	return (scale.x == scale.y && scale.x == scale.z);
}

bool CC3Node::getIsUniformlyScaledGlobally()
{
	return getIsUniformlyScaledLocally() && (parent ? parent->getIsUniformlyScaledGlobally() : true);
}

bool CC3Node::getIsTransformRigid()
{
	return (scale.x == 1.0f && scale.y == 1.0f && scale.z == 1.0f) && (parent ? parent->getIsTransformRigid() : true);
}

void CC3Node::setBoundingVolume(CC3NodeBoundingVolume* aBoundingVolume)
{
	CC3NodeBoundingVolume* oldBV = boundingVolume;
	boundingVolume = aBoundingVolume;
	aBoundingVolume->retain();
	if (oldBV) 
	{
		oldBV->release();
	}
	boundingVolume->setNode(this);
}

CC3NodeBoundingVolume* CC3Node::getBoundingVolume()
{
	return boundingVolume;
}

CC3BoundingBox CC3Node::getBoundingBox()
{
	CC3NodeBoundingBoxVisitor* bbVisitor = CC3NodeBoundingBoxVisitor::visitor();
	bbVisitor->setShouldLocalizeToStartingNode(true);
	bbVisitor->visit(this);
	//LogTrace(@"Measured %@ bounding box: %@", self, NSStringFromCC3BoundingBox(bbVisitor.boundingBox));
	return bbVisitor->getBoundingBox();
}

CC3BoundingBox CC3Node::getGlobalBoundingBox()
{
	CC3NodeBoundingBoxVisitor* bbVisitor = CC3NodeBoundingBoxVisitor::visitor();
	bbVisitor->visit(this);
	//LogTrace(@"Measured %@ global bounding box: %@", self, NSStringFromCC3BoundingBox(bbVisitor.boundingBox));
	return bbVisitor->getBoundingBox();
}

void CC3Node::setProjectedLocation(CC3Vector aLocation)
{
	projectedLocation = aLocation;
}

CC3Vector CC3Node::getProjectedLocation()
{
	return projectedLocation;
}

cocos2d::CCPoint CC3Node::getProjectedPosition()
{
	return (projectedLocation.z > 0.0)
	? ccp(projectedLocation.x, projectedLocation.y)
	: ccp(-FLT_MAX, -FLT_MAX);
}

void CC3Node::setVisible(bool bVisible)
{
	visible = bVisible;
}

bool CC3Node::getVisible()
{
	return visible && (!parent || parent->getVisible());
}

bool CC3Node::getHasLocalContent()
{
	return false;
}

bool CC3Node::getIsMeshNode()
{
	return false;
}

static GLuint lastAssignedNodeTag;

GLuint CC3Node::getNextTag()
{
	return ++lastAssignedNodeTag;
}

void CC3Node::resetTagAllocation()
{
	lastAssignedNodeTag = 0;
}

void CC3Node::setShouldCullBackFaces(bool shouldCull)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setShouldCullBackFaces(shouldCull);
	}
}

bool CC3Node::getShouldCullBackFaces()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		if (child->getShouldCullBackFaces() == false) 
		{
			return false;
		}
	}
	return true;
}

void CC3Node::setShouldCullFrontFaces(bool newValue)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setShouldCullFrontFaces(newValue);
	}
}

bool CC3Node::getShouldCullFrontFaces()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject) 
	{
		CC3Node* child = (CC3Node*)pObject;
		if (child->getShouldCullFrontFaces()) 
		{
			return true;
		}
	}
	return false;
}

bool CC3Node::getIsRunning()
{
	return m_bIsRunning;
}

void CC3Node::setIsRunning(bool shouldRun)
{
	if (!m_bIsRunning && shouldRun) resumeActions();
	if (m_bIsRunning && !shouldRun) pauseActions();
	m_bIsRunning = shouldRun;
	
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject) 
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setIsRunning(shouldRun);
	}
}

void CC3Node::setPerformanceStatistics(CC3PerformanceStatistics* newValue)
{
}

CC3PerformanceStatistics* CC3Node::getPerformanceStatistics()
{
	return NULL;
}

std::string CC3Node::getStructureDescription()
{
	return appendStructureDescriptionTo("", 0);
}

std::string CC3Node::appendStructureDescriptionTo(std::string desc, int indentLevel)
{
	desc += "\n";
	for (int i = 0; i < indentLevel; i++) 
	{
		desc += "  ";
	}
	char szSelf[256];
	sprintf(szSelf, "%d", (int)this);
	
	desc += szSelf;
	
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->appendStructureDescriptionTo(desc, indentLevel + 1);
	}
	
	return desc;
}

void CC3Node::setShouldUseLighting(bool newValue)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setShouldUseLighting(newValue);
	}
}

bool CC3Node::getShouldUseLighting()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject) 
	{
		CC3Node* child = (CC3Node*)pObject;
		if (child->getShouldUseLighting()) 
		{
			return true;
		}
	}
	return false;
}

cocos2d::ccColor4F CC3Node::getAmbientColor()
{
	cocos2d::ccColor4F col = kCCC4FBlackTransparent;
	int childCnt = 0;
	if (children && (childCnt = children->count()) > 0) 
	{
		GLfloat rSum, bSum, gSum, aSum;
		rSum = bSum = gSum = aSum = 0.0f;
		
		cocos2d::CCObject* pObject = NULL;
		CCARRAY_FOREACH(children, pObject) 
		{
			CC3Node* child = (CC3Node*)pObject;
			cocos2d::ccColor4F childColor = child->getAmbientColor();
			rSum += childColor.r;
			gSum += childColor.g;
			bSum += childColor.b;
			aSum += childColor.a;
		}
		col = CCC4FMake(rSum / childCnt, gSum / childCnt, bSum / childCnt, aSum / childCnt);
	}
	return col;
}

void CC3Node::setAmbientColor(cocos2d::ccColor4F newValue)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject) 
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setAmbientColor(newValue);
	}
}

cocos2d::ccColor4F CC3Node::getDiffuseColor()
{
	cocos2d::ccColor4F col = kCCC4FBlackTransparent;
	int childCnt = 0;
	if (children && (childCnt = children->count()) > 0) 
	{
		GLfloat rSum, bSum, gSum, aSum;
		rSum = bSum = gSum = aSum = 0.0f;
		
		cocos2d::CCObject* pObject = NULL;
		CCARRAY_FOREACH(children, pObject) 
		{
			CC3Node* child = (CC3Node*)pObject;
			cocos2d::ccColor4F childColor = child->getDiffuseColor();
			rSum += childColor.r;
			gSum += childColor.g;
			bSum += childColor.b;
			aSum += childColor.a;
		}
		col = CCC4FMake(rSum / childCnt, gSum / childCnt, bSum / childCnt, aSum / childCnt);
	}
	return col;
}

void CC3Node::setDiffuseColor(cocos2d::ccColor4F newValue)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject) 
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setDiffuseColor(newValue);
	}
}

cocos2d::ccColor4F CC3Node::getSpecularColor()
{
	cocos2d::ccColor4F col = kCCC4FBlackTransparent;
	int childCnt = 0;
	if (children && (childCnt = children->count()) > 0) 
	{
		float rSum, bSum, gSum, aSum;
		rSum = bSum = gSum = aSum = 0.0f;
		
		cocos2d::CCObject* pObject = NULL;
		CCARRAY_FOREACH(children, pObject) 
		{
			CC3Node* child = (CC3Node*)pObject;
			cocos2d::ccColor4F childColor = child->getSpecularColor();
			rSum += childColor.r;
			gSum += childColor.g;
			bSum += childColor.b;
			aSum += childColor.a;
		}
		col = CCC4FMake(rSum / childCnt, gSum / childCnt, bSum / childCnt, aSum / childCnt);
	}
	return col;
}

void CC3Node::setSpecularColor(cocos2d::ccColor4F newValue)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)  
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setSpecularColor(newValue);
	}
}

cocos2d::ccColor4F CC3Node::getEmissionColor()
{
	cocos2d::ccColor4F col = kCCC4FBlackTransparent;
	int childCnt = 0;
	if (children && (childCnt = children->count()) > 0) 
	{
		float rSum, bSum, gSum, aSum;
		rSum = bSum = gSum = aSum = 0.0f;
		
		cocos2d::CCObject* pObject = NULL;
		CCARRAY_FOREACH(children, pObject) 
		{
			CC3Node* child = (CC3Node*)pObject;
			cocos2d::ccColor4F childColor = child->getEmissionColor();
			rSum += childColor.r;
			gSum += childColor.g;
			bSum += childColor.b;
			aSum += childColor.a;
		}
		col = CCC4FMake(rSum / childCnt, gSum / childCnt, bSum / childCnt, aSum / childCnt);
	}
	return col;
}

void CC3Node::setEmissionColor(cocos2d::ccColor4F newValue)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setEmissionColor(newValue);
	}
}

CC3Vector CC3Node::getGlobalLightLocation()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		CC3Vector cgll = child->getGlobalLightLocation();
		if ( !CC3VectorsAreEqual(cgll, kCC3VectorZero) ) 
		{
			return cgll;
		}
	}
	return kCC3VectorZero;
}

void CC3Node::setGlobalLightLocation(CC3Vector newValue)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setGlobalLightLocation(newValue);
	}
}

void CC3Node::setColor(cocos2d::ccColor3B color)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setColor(color);
	}
}

cocos2d::ccColor3B CC3Node::getColor(void)
{
	cocos2d::ccColor3B col = cocos2d::ccBLACK;
	int childCnt = 0;
	if (children && (childCnt = children->count()) > 0) 
	{
		GLuint rSum, bSum, gSum;
		rSum = bSum = gSum = 0;
		
		cocos2d::CCObject* pObject = NULL;
		CCARRAY_FOREACH(children, pObject)
		{
			CC3Node* child = (CC3Node*)pObject;
			cocos2d::ccColor3B childColor = child->getColor();
			rSum += childColor.r;
			gSum += childColor.g;
			bSum += childColor.b;
		}
		col = cocos2d::ccc3(rSum / childCnt, gSum / childCnt, bSum / childCnt);
	}
	return col;
}

GLubyte CC3Node::getOpacity(void)
{
	GLubyte opc = 0;
	int childCnt = 0;
	if (children && (childCnt = children->count()) > 0) 
	{
		GLuint oSum = 0;
		
		cocos2d::CCObject* pObject = NULL;
		CCARRAY_FOREACH(children, pObject) 
		{
			CC3Node* child = (CC3Node*)pObject;
			oSum += child->getOpacity();
		}
		opc = oSum / childCnt;
	}
	return opc;
}

void CC3Node::setOpacity(GLubyte opacity)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject) 
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setOpacity(opacity);
	}
}

bool CC3Node::getIsOpaque()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		if(!child->getIsOpaque()) 
		{
			return false;
		}
	}
	return true;
}

void CC3Node::setIsOpaque(bool newValue)
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject) 
	{
		CC3Node* child = (CC3Node*)pObject;
		child->setIsOpaque(newValue);
	}
}

bool CC3Node::initWithTag(GLuint aTag, const char* aName) 
{
	CC3Identifiable::initWithTag(aTag, aName);
	{
		transformMatrixInverted = NULL;
		globalRotationMatrix = NULL;
		rotator = rotatorClass();
		rotator->retain();
		boundingVolume = NULL;
		setLocation(kCC3VectorZero);
		setGlobalLocation(kCC3VectorZero);
		setProjectedLocation(kCC3VectorZero);
		setScale(kCC3VectorUnitCube);
		setGlobalScale(kCC3VectorUnitCube);
		isTransformDirty = false;			// everything starts out at identity
		isTouchEnabled = false;
		shouldInheritTouchability = true;
		isAnimationEnabled = true;
		visible = true;
		setIsRunning(false);
		setShouldAutoremoveWhenEmpty(false);
		setTransformMatrix(CC3GLMatrix::identity());		// Has side effects...so do last (transformMatrixInverted is built in some subclasses)
	}
	
	return true;
}

CC3Node* CC3Node::node()
{
	CC3Node* pNode = new CC3Node;
	if (pNode) 
	{
		pNode->autorelease();
	}
	
	return pNode;
}

CC3Node* CC3Node::nodeWithTag(GLuint aTag)
{
	CC3Node* pNode = new CC3Node;
	if (pNode) 
	{
		pNode->initWithTag(aTag, pNode->getName().c_str());
		pNode->autorelease();
	}
	
	return pNode;
}

CC3Node* CC3Node::nodeWithName(const char* aName)
{
	CC3Node* pNode = new CC3Node;
	if (pNode) 
	{
		pNode->initWithName(aName);
		pNode->autorelease();
	}
	
	return pNode;
}

CC3Node* CC3Node::nodeWithTag(GLuint aTag, const char* aName)
{
	CC3Node* pNode = new CC3Node;
	if (pNode) 
	{
		pNode->initWithTag(aTag, aName);
		pNode->autorelease();
	}
	
	return pNode;
}

CC3Node* CC3Node::copy()
{
	// todo
	this->retain();
	return this;
}

CC3Node* CC3Node::copyWithName(const char* aName)
{
	// todo
	this->retain();
	return this;
}

void CC3Node::createGLBuffers()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->createGLBuffers();
	}
}

void CC3Node::deleteGLBuffers()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->deleteGLBuffers();
	}
}

void CC3Node::releaseRedundantData()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->releaseRedundantData();
	}
}

void CC3Node::retainVertexLocations()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->retainVertexLocations();
	}
}

void CC3Node::retainVertexNormals()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->retainVertexNormals();
	}
}

void CC3Node::retainVertexColors()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->retainVertexColors();
	}
}

void CC3Node::retainVertexTextureCoordinates()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->retainVertexTextureCoordinates();
	}
}

void CC3Node::retainVertexIndices()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->retainVertexIndices();
	}
}

void CC3Node::doNotBufferVertexLocations()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->doNotBufferVertexLocations();
	}
}

void CC3Node::doNotBufferVertexNormals()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->doNotBufferVertexNormals();
	}
}

void CC3Node::doNotBufferVertexColors()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->doNotBufferVertexColors();
	}
}

void CC3Node::doNotBufferVertexTextureCoordinates()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->doNotBufferVertexTextureCoordinates();
	}
}

void CC3Node::doNotBufferVertexIndices()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->doNotBufferVertexIndices();
	}
}

void CC3Node::alignTextures()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->alignTextures();
	}
}

void CC3Node::alignInvertedTextures()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->alignInvertedTextures();
	}
}

void CC3Node::updateBeforeTransform(CC3NodeUpdatingVisitor* visitor)
{
	updateBeforeChildren(visitor);
	update(visitor->getDeltaTime());
}

void CC3Node::updateAfterTransform(CC3NodeUpdatingVisitor* visitor)
{
	updateAfterChildren(visitor);
}

void CC3Node::setTransformMatrix(CC3GLMatrix* aCC3GLMatrix)
{
	if (transformMatrix != aCC3GLMatrix) 
	{
		if (transformMatrix) 
		{
			transformMatrix->release();
		}
		
		transformMatrix = aCC3GLMatrix;
		aCC3GLMatrix->retain();
		updateGlobalOrientation();
		transformMatrixChanged();
	}
}

CC3GLMatrix* CC3Node::getTransformMatrix()
{
	return transformMatrix;
}

bool CC3Node::getIsTransformDirty()
{
	return isTransformDirty;
}

void CC3Node::markTransformDirty()
{
	isTransformDirty = true;
}

CC3GLMatrix* CC3Node::getTransformMatrixInverted()
{
	if (!transformMatrixInverted) 
	{
		transformMatrixInverted = CC3GLMatrix::matrix();
		transformMatrixInverted->retain();
		isTransformInvertedDirty = true;
	}
	
	if (isTransformInvertedDirty) 
	{
		transformMatrixInverted->populateFrom(transformMatrix);
		
		// If the transform is rigid (only rotation & translation), use faster inversion.
		// It would be better to move this test to the matrix itself, but there is no
		// known quick and accurate test to tell if a matrix represents a rigid transform.
		if (getIsTransformRigid()) 
		{
			transformMatrixInverted->invertRigid();
		} 
		else 
		{
			transformMatrixInverted->invertAffine();
		}
		isTransformInvertedDirty = false;
		
		//LogTrace(@"%@ transform %@ inverted %@to %@", self, transformMatrix,
//				 (self.isTransformRigid ? @"rigidly " : @""), transformMatrixInverted);
	}
	
	return transformMatrixInverted;
}

void CC3Node::updateTransformMatrices()
{
	transformVisitorClass()->visit(this);
}

CC3NodeTransformingVisitor* CC3Node::transformVisitorClass()
{
	return CC3NodeTransformingVisitor::visitor();
}

void CC3Node::updateTransformMatrix()
{
	CC3NodeTransformingVisitor* visitor = transformVisitorClass();
	visitor->setShouldVisitChildren(false);
	visitor->visit(this);
}

void CC3Node::buildTransformMatrixWithVisitor(CC3NodeTransformingVisitor* visitor)
{
	CC3GLMatrix* pMtx = visitor->parentTansformMatrixFor(this);
	if (pMtx) 
	{
		transformMatrix->populateFrom(pMtx);
	} 
	else 
	{
		transformMatrix->populateIdentity();
	}
	
	applyLocalTransforms();
	transformMatrixChanged();
}

void CC3Node::drawWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	if (getVisible() && getHasLocalContent() && doesIntersectFrustum(visitor->getFrustum())) 
	{
		transformAndDrawWithVisitor(visitor);
	}
}

void CC3Node::transformAndDrawWithVisitor(CC3NodeDrawingVisitor* visitor)
{
	//LogTrace(@"Drawing %@", self);
	CC3OpenGLES11MatrixStack* gles11MatrixStack = CC3OpenGLES11Engine::engine()->getMatrices()->getModelview();
	
	gles11MatrixStack->push();
	
	//LogTrace(@"%@ applying transform matrix: %@", self, transformMatrix);
	gles11MatrixStack->multiply(transformMatrix->getGlMatrix());
	
	visitor->drawLocalContentOf(this);
	
	gles11MatrixStack->pop();
}

bool CC3Node::doesIntersectFrustum(CC3Frustum* aFrustum)
{
	if (boundingVolume && aFrustum) 
	{
		bool intersects = boundingVolume->doesIntersectFrustum(aFrustum);
		//LogTrace(@"%@ bounded by %@ %@\n%@", self, boundingVolume,
//				 (intersects ? @"intersects" : @"does not intersect"), aFrustum);
		
		// Uncomment and change name to verify culling:
		//		if (!intersects && ([self.name isEqualToString: @"MyNodeName"])) {
		//			LogDebug(@"%@ does not intersect\n%@", self, aFrustum);
		//		}
		return intersects;
	}
	
	return true;
}

void CC3Node::checkDrawingOrder()
{
	cocos2d::CCObject* pObject = NULL;
	CCARRAY_FOREACH(children, pObject)
	{
		CC3Node* child = (CC3Node*)pObject;
		child->checkDrawingOrder();
	}
}

void CC3Node::setChildren(cocos2d::CCArray* newValue)
{
	if (newValue == children) 
	{
		return;
	}
	
	if (children) 
	{
		children->release();
	}
	
	children = newValue;
	
	if (children) 
	{
		children->retain();
	}
}

cocos2d::CCArray* CC3Node::getChildren()
{
	return children;
}

void CC3Node::setParent(CC3Node* newValue)
{
	parent = newValue;
	markTransformDirty();
}

CC3Node* CC3Node::getParent()
{
	return parent;
}

//void CC3Node::setRootAncestor(CC3Node* newValue)
//{
//	rootAncestor = newValue;
//}

CC3Node* CC3Node::getRootAncestor()
{
	return parent ? parent->getRootAncestor() : this;
}

void CC3Node::setShouldAutoremoveWhenEmpty(bool newValue)
{
	shouldAutoremoveWhenEmpty = newValue;
}

bool CC3Node::getShouldAutoremoveWhenEmpty()
{
	return shouldAutoremoveWhenEmpty;
}

void CC3Node::addChild(CC3Node* aNode)
{
	//cocos2d::CCAssert(aNode, "Child CC3Node cannot be nil");
	if(aNode->getParent() == this) return;
	
	aNode->remove();				// Remove node from any existing parent
	if(!children) 
	{
		children = cocos2d::CCArray::array();
		children->retain();
	}
	children->addObject((CCObject*)aNode);
	aNode->setParent(this);
	aNode->setIsRunning(getIsRunning());
	didAddDescendant(aNode);
	//LogTrace(@"After adding %@, %@ now has children: %@", aNode, self, children);
}

void CC3Node::didAddDescendant(CC3Node* aNode)
{
	parent->didAddDescendant((CC3Node*)aNode);
}

void CC3Node::addAndLocalizeChild(CC3Node* aNode)
{
	CC3GLMatrix* g2LMtx;		// Global to local transformation matrix
	
	// Since this calculation depends both the parent and child transformMatrixes,
	// make sure they are up to date.
	updateTransformMatrix();
	aNode->updateTransformMatrix();
	
	// Localize the child node's location by finding the right local matrix,
	// and then translating the child node's local origin by the resulting matrix.
	// This is what the location property does. It instructs the local matrix
	// to move the node's origin. By transforming the origin, we determine what
	// that location property needs to be.
	g2LMtx = CC3GLMatrix::matrix();
	g2LMtx->populateFrom(getTransformMatrixInverted());
	g2LMtx->multiplyByMatrix(aNode->getTransformMatrix());
	aNode->setLocation(g2LMtx->transformLocation(kCC3VectorZero));
	
	// Localize the child node's rotation by finding the right rotation matrix.
	// For rotation, we use the globalRotationMatrix, which is free of scale
	// and translation content. Otherwise it would be impossible to extract
	// the local rotation from an arbitrarily scaled and translated matrix.
	g2LMtx->release();
	g2LMtx = CC3GLMatrix::matrix();
	g2LMtx->populateFrom(getGlobalRotationMatrix());
	g2LMtx->invertRigid();		// Only contains rotation...so it's rigid.
	g2LMtx->multiplyByMatrix(aNode->getGlobalRotationMatrix());
	aNode->setRotation(g2LMtx->extractRotation());
	
	// Scale cannot readily be extracted from the inverted and multiplied matrix,
	// but we can get it by scaling the node's scale down by the globalScale
	// of this parent, so that when they are recombined, the original globalScale
	// of the child node.
	aNode->setScale(CC3VectorScale(aNode->getGlobalScale(), CC3VectorInvert(getGlobalScale())));
	
	addChild(aNode);		// Finally, add the child node to this parent
}

CC3GLMatrix* CC3Node::getGlobalRotationMatrix()
{
	if (!globalRotationMatrix) 
	{
		globalRotationMatrix = CC3GLMatrix::matrix();
		globalRotationMatrix->retain();
		isGlobalRotationDirty = true;
	}
	
	if (isGlobalRotationDirty) 
	{
		if (parent) 
		{
			globalRotationMatrix->populateFrom(parent->getGlobalRotationMatrix());
			globalRotationMatrix->multiplyByMatrix(rotator->getRotationMatrix());
		} 
		else 
		{
			globalRotationMatrix->populateFrom(rotator->getRotationMatrix());
		}
		isGlobalRotationDirty = false;
	}
	
	return globalRotationMatrix;
}

void CC3Node::removeChild(CC3Node* aNode)
{
	if (children && aNode) 
	{
		int indx = children->indexOfObject((CCObject*)aNode);
		if (indx != UINT_MAX) 
		{
			
			// If the children collection is the only thing referencing the child node, the
			// child node will be deallocated as soon as it is removed, and will be invalid
			// when passed to the didRemoveDescendant: method, or to other activities that
			// it may be subject to in the processing loop. To avoid problems, retain it for
			// the duration of this processing loop, so that it will still be valid until
			// we're done with it.
			
			// todo
			//[[aNode retain] autorelease];
			aNode->retain();
			
			aNode->setParent(NULL);
			children->removeObjectAtIndex(indx);
			
			if (children->count() == 0) 
			{
				children->release();
				children = NULL;
			}
			
			aNode->setIsRunning(false);
			didRemoveDescendant(aNode);
		}
		//LogTrace(@"After removing %@, %@ now has children: %@", aNode, self, children);
		
		// If the last child has been removed, and this instance should autoremove when
		// that occurs, remove this node from the hierarchy as well. This must be performed
		// after everything else is done, particularly only after the didRemoveDescendant:
		// has been invoked so that that notification can propagate up the node hierarchy.
		if (!children && shouldAutoremoveWhenEmpty) 
		{
			remove();
		}
	}
}

void CC3Node::didRemoveDescendant(CC3Node* aNode)
{
	parent->didRemoveDescendant((CC3Node*)aNode);
}

void CC3Node::removeAllChildren()
{
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		removeChild(pNode);
	}
	
	if (children) 
	{
		children->release();
	}
}

void CC3Node::remove()
{
	if (parent) 
	{
		parent->removeChild(this);
	}
}

CC3Node* CC3Node::getNodeNamed(const char* aName)
{
	if (strcmp(name.c_str(), aName) == 0 || (name.size() == 0 && !aName)) 
	{	// my name equal or both nil
		return this;
	}
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		CC3Node* childResult = pNode->getNodeNamed(aName);
		if (childResult) 
		{
			return childResult;
		}
	}
	
	return NULL;
}

CC3Node* CC3Node::getNodeTagged(GLuint aTag)
{
	if (tag == aTag) 
	{
		return this;
	}
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		CC3Node* childResult = pNode->getNodeTagged(aTag);
		if (childResult) 
		{
			return childResult;
		}
	}
	
	return NULL;
}

bool CC3Node::isDescendantOf(CC3Node* aNode)
{
	return parent ? (parent == aNode || parent->isDescendantOf(aNode)) : false;
}

cocos2d::CCArray* CC3Node::flatten()
{
	cocos2d::CCArray* allNodes = cocos2d::CCArray::array();
	flattenInto(allNodes);
	
	return allNodes;
}

void CC3Node::flattenInto(cocos2d::CCArray* anArray)
{
	anArray->addObject((CCObject*)this);
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->flattenInto(anArray);
	}
}

cocos2d::CCAction* CC3Node::runAction(cocos2d::CCAction* action)
{
	//cocos2d::CCAssert( action != NULL, "Argument must be non-nil");
	// todo 第二个参数不对
	cocos2d::CCActionManager::sharedManager()->addAction(action, (cocos2d::CCNode*)this, !m_bIsRunning);
	return action;
}

void CC3Node::stopAllActions()
{
	cocos2d::CCActionManager::sharedManager()->removeAllActionsFromTarget(this);
}

void CC3Node::stopAction(cocos2d::CCAction* action)
{
	cocos2d::CCActionManager::sharedManager()->removeAction(action);
}

void CC3Node::stopActionByTag(int aTag)
{
	//cocos2d::CCAssert( aTag != kCCActionTagInvalid, "Invalid tag");
	cocos2d::CCActionManager::sharedManager()->removeActionByTag(aTag, this);
}

cocos2d::CCAction* CC3Node::getActionByTag(int aTag)
{
	//cocos2d::CCAssert(aTag != cocos2d::kCCActionTagInvalid, "Invalid tag");
	return cocos2d::CCActionManager::sharedManager()->getActionByTag(aTag, this);
}

void CC3Node::cleanup()
{
	stopAllActions();
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->cleanup();
	}
}

int CC3Node::numberOfRunningActions()
{
	return cocos2d::CCActionManager::sharedManager()->numberOfRunningActionsInTarget((CCObject*)this);
}

void CC3Node::setIsTouchEnabled(bool newValue)
{
	isTouchEnabled = newValue;
}

bool CC3Node::getIsTouchEnabled()
{
	return isTouchEnabled;
}

bool CC3Node::getIsTouchable()
{
	return isTouchEnabled || ((parent && shouldInheritTouchability) ? parent->getIsTouchable() : false);
}

void CC3Node::setShouldInheritTouchability(bool newValue)
{
	shouldInheritTouchability = newValue;
}

bool CC3Node::getShouldInheritTouchability()
{
	return shouldInheritTouchability;
}

CC3Node* CC3Node::getTouchableNode()
{
	return isTouchEnabled ? this : (parent ? parent->getTouchableNode() : NULL);
}

void CC3Node::touchEnableAll()
{
	isTouchEnabled = true;
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->touchEnableAll();
	}
}

void CC3Node::touchDisableAll()
{
	isTouchEnabled = false;
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->touchDisableAll();
	}
}

void CC3Node::setAnimation(CC3NodeAnimation* newValue)
{
	animation = newValue;
	animation->retain();
}

CC3NodeAnimation* CC3Node::getAnimation()
{
	return animation;
}

bool CC3Node::getContainsAnimation()
{
	if (animation) 
	{
		return true;
	}
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		if (pNode->getContainsAnimation()) 
		{
			return true;
		}
	}
	return false;
}

void CC3Node::setIsAnimationEnabled(bool newValue)
{
	isAnimationEnabled = newValue;
}

bool CC3Node::getIsAnimationEnabled()
{
	return isAnimationEnabled;
}

void CC3Node::enableAnimation()
{
	isAnimationEnabled = true;
}

void CC3Node::disableAnimation()
{
	isAnimationEnabled = false;
}

void CC3Node::enableAllAnimation()
{
	enableAnimation();
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->enableAllAnimation();
	}
}

void CC3Node::disableAllAnimation()
{
	disableAnimation();
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->disableAllAnimation();
	}
}

void CC3Node::establishAnimationFrameAt(cocos2d::ccTime t)
{
	if (animation && isAnimationEnabled) 
	{
		//LogTrace(@"%@ animating frame at %.3f ms", self, t);
		animation->establishFrameAt(t, this);
	}
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->establishAnimationFrameAt(t);
	}
}

void CC3Node::setShouldDrawDescriptor(bool shouldDraw)
{
	// Fetch the descriptor node from the child nodes.
	CC3NodeDescriptor* dn = getDescriptorNode();
	
	// If the descriptor node exists, but should not, remove it
	if (dn && !shouldDraw) 
	{
		dn->remove();
	}
	
	// If there is no descriptor node, but there should be, add it by creating a
	// CC3NodeDescriptor from the description property and add it as a child of this node.
	if(!dn && shouldDraw) 
	{
		cocos2d::CCLabelTTF* dnLabel = cocos2d::CCLabelTTF::labelWithString("?"/*getDescription()*/,
																			"Arial",
																			getDescriptorFontSize());
		dn = CC3NodeDescriptor::nodeWithName(getDescriptorName(), dnLabel);
		dn->setColor(getInitialDescriptorColor());
		addChild(dn);
	}
}

bool CC3Node::getShouldDrawDescriptor()
{
	return (getDescriptorNode() != NULL);
}

CC3NodeDescriptor* CC3Node::getDescriptorNode()
{
	return (CC3NodeDescriptor*)getNodeNamed(getDescriptorName());
}

#define kDescriptorSuffix "DESC"

const char* CC3Node::getDescriptorName()
{
	static char szName[256];
	sprintf(szName, "%s-%i-%s", getName().c_str(), getTag(), kDescriptorSuffix);
	
	return szName;
}

bool CC3Node::getShouldDrawAllDescriptors()
{
	if (!getShouldDrawDescriptor()) 
	{
		return false;
	}
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		if (!pNode->getShouldDrawAllDescriptors()) 
		{
			return false;
		}
	}
	
	return true;
}

void CC3Node::setShouldDrawAllDescriptors(bool newValue)
{
	setShouldDrawDescriptor(newValue);
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->setShouldDrawAllDescriptors(newValue);
	}
}

static float descriptorFontSize = 14.0;

float CC3Node::getDescriptorFontSize()
{
	return descriptorFontSize;
}

void CC3Node::setDescriptorFontSize(float fontSize)
{
	descriptorFontSize = fontSize;
}

bool CC3Node::getShouldDrawWireframeBox()
{
	return (getWireframeBoxNode() != NULL);
}

void CC3Node::setShouldDrawWireframeBox(bool shouldDraw)
{
	// Fetch the wireframe node from the child nodes.
	CC3WireframeBoundingBoxNode* wf = getWireframeBoxNode();
	
	// If the wireframe exists, but should not, remove it
	if (wf && !shouldDraw) 
	{
		wf->remove();
	}
	
	// If there is no wireframe, but there should be, add it by creating a
	// CC3WireframeBoundingBoxNode from the boundingBox property and add it as a
	// child of this node. If the bounding box is null, don't create a wireframe.
	// The bounding box is set to update its vertices from the bounding box of
	// this node on each update pass to allow the wireframe to grow and shrink
	// along with the bounding box of this node and its descendants
	if(!wf && shouldDraw) 
	{
		CC3BoundingBox gbb = getBoundingBox();
		if ( !CC3BoundingBoxIsNull(gbb) ) 
		{
			wf = CC3WireframeBoundingBoxNode::nodeWithName(getWireframeBoxName());
			wf->populateAsWireBox(gbb);
			wf->setPureColor(getInitialWireframeBoxColor());
			wf->setShouldAlwaysMeasureParentBoundingBox(true);
			addChild(wf);
		}
	}
}

CC3WireframeBoundingBoxNode* CC3Node::getWireframeBoxNode()
{
	return (CC3WireframeBoundingBoxNode*)getNodeNamed(getWireframeBoxName());
}

// The default color to use when drawing the wireframes
static cocos2d::ccColor4F wireframeBoxColor = { 1.0, 1.0, 0.0, 1.0 };	// kCCC4FYellow

cocos2d::ccColor4F CC3Node::getWireframeBoxColor()
{
	return wireframeBoxColor;
}

void CC3Node::setWireframeBoxColor(cocos2d::ccColor4F aColor)
{
	wireframeBoxColor = aColor;
}

bool CC3Node::getShouldDrawAllWireframeBoxes()
{
	return false;
}

void CC3Node::setShouldDrawAllWireframeBoxes(bool newValue)
{
	setShouldDrawWireframeBox(newValue);
	
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->setShouldDrawAllWireframeBoxes(newValue);
	}
}

void CC3Node::setShouldDrawAllLocalContentWireframeBoxes(bool newValue)
{
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		pNode->setShouldDrawAllLocalContentWireframeBoxes(newValue);
	}
}

bool CC3Node::getShouldDrawAllLocalContentWireframeBoxes()
{
	cocos2d::CCObject* child = NULL;
	CCARRAY_FOREACH(children, child)
	{
		CC3Node* pNode = (CC3Node*)child;
		if (!pNode->getShouldDrawAllLocalContentWireframeBoxes()) 
		{
			return false;
		}
	}
	return true;
}

void CC3Node::applyLocalTransforms()
{
	applyTranslation();
	applyRotation();
	applyScaling();
}

void CC3Node::applyTranslation()
{
	transformMatrix->translateBy(location);
	updateGlobalLocation();
	//LogTrace(@"%@ translated to %@, globally %@ %@", self, NSStringFromCC3Vector(location),
//			 NSStringFromCC3Vector(globalLocation), transformMatrix);
}

void CC3Node::applyRotation()
{
	rotator->applyRotationTo(transformMatrix);
	updateGlobalRotation();
	//LogTrace(@"%@ rotated to %@ %@", self, NSStringFromCC3Vector(rotator.rotation), transformMatrix);
}

void CC3Node::applyScaling()
{
	transformMatrix->scaleBy(scale);
	updateGlobalScale();
	//LogTrace(@"%@ scaled to %@, globally %@ %@", self, NSStringFromCC3Vector(scale),
//			 NSStringFromCC3Vector(globalScale), transformMatrix);
}

void CC3Node::transformMatrixChanged()
{
	updateBoundingVolume();
	isTransformDirty = false;
	isTransformInvertedDirty = true;
}

void CC3Node::updateGlobalOrientation()
{
	updateGlobalLocation();
	updateGlobalRotation();
	updateGlobalScale();
}

void CC3Node::updateGlobalLocation()
{
	globalLocation = transformMatrix->transformLocation(kCC3VectorZero);
}

void CC3Node::updateGlobalRotation()
{
	isGlobalRotationDirty = true;
}

void CC3Node::updateGlobalScale()
{
	globalScale = parent ? CC3VectorScale(parent->getGlobalScale(), scale) : scale;
}

void CC3Node::updateBoundingVolume()
{
	if (boundingVolume) 
	{
		boundingVolume->update();
	}
}

void CC3Node::drawLocalContentWithVisitor(CC3NodeDrawingVisitor* visitor)
{
}

void CC3Node::descendantDidModifySequencingCriteria(CC3Node* aNode)
{
	parent->descendantDidModifySequencingCriteria(aNode);
}

CC3Rotator* CC3Node::rotatorClass()
{
	return CC3Rotator::rotator();
}

void CC3Node::populateFrom(CC3Node* another)
{
	CC3Identifiable::populateFrom((CC3Identifiable*)another);
	
	// Bypass setter method to avoid side effects.
	transformMatrix->release();
	transformMatrix = another->getTransformMatrix();
	transformMatrix->retain();//->copy();		// retained
	
	isTransformInvertedDirty = true;							// create or rebuild lazily
	isGlobalRotationDirty = true;							// create or rebuild lazily
	
	rotator->release();
	rotator = another->getRotator();
	rotator->retain();//[another.rotator copy];						// retained
	
	boundingVolume->release();
	boundingVolume = another->getBoundingVolume();
	boundingVolume->retain();// copy];			// retained
	boundingVolume->setNode(this);
	
	animation->release();
	animation = another->getAnimation();
	animation->retain();//[another.animation retain];					// retained...not copied
	
	location = another->getLocation();
	globalLocation = another->getGlobalLocation();
	projectedLocation = another->getProjectedLocation();
	scale = another->getScale();
	globalScale = another->getGlobalScale();
	isTransformDirty = another->getIsTransformDirty();
	isTouchEnabled = another->getIsTouchEnabled();
	shouldInheritTouchability = another->getShouldInheritTouchability();
	isAnimationEnabled = another->getIsAnimationEnabled();
	visible = another->getVisible();
	m_bIsRunning = another->getIsRunning();
	shouldAutoremoveWhenEmpty = another->getShouldAutoremoveWhenEmpty();
	setShouldDrawDescriptor(another->getShouldDrawDescriptor());		// May create a child node
	setShouldDrawWireframeBox(another->getShouldDrawWireframeBox());	// May create a child node
}

void CC3Node::copyChildrenFrom(CC3Node* another)
{
	removeAllChildren();
	cocos2d::CCArray* otherKids = another->getChildren();
	if (otherKids) 
	{
		cocos2d::CCObject* child = NULL;
		CCARRAY_FOREACH(children, child)
		{
			CC3Node* pNode = (CC3Node*)child;
			// todo
			//if (pNode->getShouldIncludeInDeepCopy()) 
//			{
//				addChild: [n copyAutoreleased]];	// retained by collection
//			}
		}
	}
}

void CC3Node::resumeActions()
{
	cocos2d::CCActionManager::sharedManager()->resumeTarget(this);
}

void CC3Node::pauseActions()
{
	cocos2d::CCActionManager::sharedManager()->pauseTarget(this);
}

CC3Rotator* CC3Node::getRotator()
{
	return rotator;
}

cocos2d::ccColor4F CC3Node::getInitialWireframeBoxColor()
{
	cocos2d::ccColor4F defaultColor = getWireframeBoxColor();
	return CCC4FAreEqual(defaultColor, kCCC4FBlackTransparent)
	? cocos2d::ccc4FFromccc3B(getColor()) 
	: defaultColor;
}

cocos2d::ccColor3B CC3Node::getInitialDescriptorColor()
{
	return CCC3BFromCCC4F(getInitialWireframeBoxColor());
}

#define kWireframeBoxSuffix "WFB"

const char* CC3Node::getWireframeBoxName()
{
	static char szName[256];
	sprintf(szName, "%s-%i-%s", getName().c_str(), getTag(), kWireframeBoxSuffix);
	
	return szName;
}

CC3TargettingNode* CC3Node::asTargettingNode()
{
	char szName[256];
	sprintf(szName, "%s-TargettingWrapper", getName().c_str());
	CC3TargettingNode* tn = CC3TargettingNode::nodeWithName(szName);
	tn->setShouldAutoremoveWhenEmpty(true);
	tn->addChild(this);
	return tn;
}

CC3TargettingNode* CC3Node::asTracker()
{
	CC3TargettingNode* tn = asTargettingNode();
	tn->setShouldTrackTarget(true);
	return tn;
}

CC3TargettingNode* CC3Node::asCameraTracker()
{
	CC3TargettingNode* tn = asTracker();
	tn->setShouldAutotargetCamera(true);
	return tn;
}

CC3TargettingNode* CC3Node::asLightTracker()
{
	char szName[256];
	sprintf(szName, "%s-LightTrackerWrapper", getName().c_str());
	CC3TargettingNode* tn = CC3LightTracker::nodeWithName(szName);
	tn->setShouldAutoremoveWhenEmpty(true);
	tn->setShouldTrackTarget(true);
	tn->addChild(this);
	return tn;
}

///////////////////////////////////////////////////////////////////////

//@interface CC3Identifiable (TemplateMethods)
//-(void) populateFrom: (CC3Identifiable*) another;
//@end
//
//// Template methods that can be overridden and invoked by subclasses
//@interface CC3Node (TemplateMethods)
//-(void) applyLocalTransforms;
//-(void) applyTranslation;
//-(void) applyRotation;
//-(void) applyScaling;
//-(id) transformVisitorClass;
//-(void) transformMatrixChanged;
//-(void) updateGlobalOrientation;
//-(void) updateGlobalLocation;
//-(void) updateGlobalRotation;
//-(void) updateGlobalScale;
//-(void) updateBoundingVolume;
//-(void) transformAndDrawWithVisitor: (CC3NodeDrawingVisitor*) visitor;
//-(void) drawLocalContentWithVisitor: (CC3NodeDrawingVisitor*) visitor;
//-(void) didAddDescendant: (CC3Node*) aNode;
//-(void) didRemoveDescendant: (CC3Node*) aNode;
//-(void) descendantDidModifySequencingCriteria: (CC3Node*) aNode;
//-(id) rotatorClass;
//-(void) populateFrom: (CC3Node*) another;
//-(void) copyChildrenFrom: (CC3Node*) another;
//-(void) resumeActions;
//-(void) pauseActions;
//@property(nonatomic, readonly) CC3Rotator* rotator;
//@property(nonatomic, readonly) CC3GLMatrix* globalRotationMatrix;
//@property(nonatomic, readonly) ccColor4F initialWireframeBoxColor;
//@end
//
//@implementation CC3Node
//
//@synthesize location, scale, globalLocation, globalScale;
//@synthesize boundingVolume, projectedLocation, transformMatrix, animation, isRunning;
//@synthesize visible, isTouchEnabled, shouldInheritTouchability, isAnimationEnabled;
//@synthesize parent, children, shouldAutoremoveWhenEmpty;
//
//-(void) dealloc {
//	[children release];
//	parent = nil;						// not retained
//	[transformMatrix release];
//	[transformMatrixInverted release];
//	[globalRotationMatrix release];
//	[rotator release];
//	[boundingVolume release];
//	[animation release];
//	[super dealloc];
//}
//
//-(void) setLocation: (CC3Vector) aLocation {
//	location = aLocation;
//	[self markTransformDirty];
//}
//
//-(CC3Vector) rotation {
//	return rotator.rotation;
//}
//
//-(void) setRotation: (CC3Vector) aRotation {
//	rotator.rotation = aRotation;
//	[self markTransformDirty];
//}
//
//-(CC3Vector4) quaternion {
//	return rotator.quaternion;
//}
//
//-(void) setQuaternion: (CC3Vector4) aQuaternion {
//	rotator.quaternion = aQuaternion;
//	[self markTransformDirty];
//}
//
//-(CC3Vector) rotationAxis {
//	return rotator.rotationAxis;
//}
//
//-(void) setRotationAxis: (CC3Vector) aDirection {
//	rotator.rotationAxis = aDirection;
//	[self markTransformDirty];
//}
//
//-(GLfloat) rotationAngle {
//	return rotator.rotationAngle;
//}
//
//-(void) setRotationAngle: (GLfloat) anAngle {
//	rotator.rotationAngle = anAngle;
//	[self markTransformDirty];
//}
//
//-(CC3Vector) globalRotation {
//	return [self.globalRotationMatrix extractRotation];
//}
//
//-(void) setScale: (CC3Vector) aScale {
//	scale = aScale;
//	[self markTransformDirty];
//}
//
//-(GLfloat) uniformScale {
//	if (self.isUniformlyScaledLocally) {
//		return scale.x;
//	} else {
//		return CC3VectorLength(scale) / kCC3VectorUnitCubeLength;
//	}
//}
//
//-(void) setUniformScale:(GLfloat) aValue {
//	self.scale = cc3v(aValue, aValue, aValue);
//}
//
//-(BOOL) isUniformlyScaledLocally {
//	return (scale.x == scale.y && scale.x == scale.z);
//}
//
//-(BOOL) isUniformlyScaledGlobally {
//	return self.isUniformlyScaledLocally && (parent ? parent.isUniformlyScaledGlobally : YES);
//}
//
//-(BOOL) isTransformRigid {
//	return (scale.x == 1.0f && scale.y == 1.0f && scale.z == 1.0f) && (parent ? parent.isTransformRigid : YES);
//}
//
//-(void) setBoundingVolume:(CC3NodeBoundingVolume *) aBoundingVolume {
//	id oldBV = boundingVolume;
//	boundingVolume = [aBoundingVolume retain];
//	[oldBV release];
//	boundingVolume.node = self;
//}
//
//// Derived from projected location, but only if in front of the camera
//-(CGPoint) projectedPosition {
//	return (projectedLocation.z > 0.0)
//				? ccp(projectedLocation.x, projectedLocation.y)
//				: ccp(-CGFLOAT_MAX, -CGFLOAT_MAX);
//}
//
//-(void) setIsRunning: (BOOL) shouldRun {
//	if (!isRunning && shouldRun) [self resumeActions];
//	if (isRunning && !shouldRun) [self pauseActions];
//	isRunning = shouldRun;
//	for (CC3Node* child in children) {
//		child.isRunning = isRunning;
//	}
//}
//
//-(BOOL) shouldCullBackFaces {
//	for (CC3Node* child in children) {
//		if (child.shouldCullBackFaces == NO) {
//			return NO;
//		}
//	}
//	return YES;
//}
//
//-(void) setShouldCullBackFaces: (BOOL) shouldCull {
//	for (CC3Node* child in children) {
//		child.shouldCullBackFaces = shouldCull;
//	}
//}
//
//-(BOOL) shouldCullFrontFaces {
//	for (CC3Node* child in children) {
//		if (child.shouldCullFrontFaces) {
//			return YES;
//		}
//	}
//	return NO;
//}
//
//-(void) setShouldCullFrontFaces: (BOOL) shouldCull {
//	for (CC3Node* child in children) {
//		child.shouldCullFrontFaces = shouldCull;
//	}
//}
//
//// Creates a specialized transforming visitor that traverses the node hierarchy below
//// this node, accumulating a bounding box that surrounds all descendant nodes.
//-(CC3BoundingBox) boundingBox {
//	CC3NodeBoundingBoxVisitor* bbVisitor = [CC3NodeBoundingBoxVisitor visitor];
//	bbVisitor.shouldLocalizeToStartingNode = YES;
//	[bbVisitor visit: self];
//	LogTrace(@"Measured %@ bounding box: %@", self, NSStringFromCC3BoundingBox(bbVisitor.boundingBox));
//	return bbVisitor.boundingBox;
//}
//
//// Creates a specialized transforming visitor that traverses the node hierarchy below
//// this node, accumulating a bounding box that surrounds all descendant nodes.
//-(CC3BoundingBox) globalBoundingBox {
//	CC3NodeBoundingBoxVisitor* bbVisitor = [CC3NodeBoundingBoxVisitor visitor];
//	[bbVisitor visit: self];
//	LogTrace(@"Measured %@ global bounding box: %@", self, NSStringFromCC3BoundingBox(bbVisitor.boundingBox));
//	return bbVisitor.boundingBox;
//}
//
//// By default, individual nodes do not collect their own performance statistics
//-(CC3PerformanceStatistics*) performanceStatistics { return nil; }
//-(void) setPerformanceStatistics: (CC3PerformanceStatistics*) aPerfStats {}
//
//-(NSString*) fullDescription {
//	return [NSString stringWithFormat: @"%@ location: %@, global: %@, %@, scale: %@, projected to: %@, bounded by: %@",
//			[super description],
//			NSStringFromCC3Vector(self.location),
//			NSStringFromCC3Vector(self.globalLocation),
//			rotator,
//			NSStringFromCC3Vector(self.scale),
//			NSStringFromCGPoint(self.projectedPosition),
//			boundingVolume];
//}
//
//-(NSString*) structureDescription {
//	return [self appendStructureDescriptionTo: [NSMutableString stringWithCapacity: 1000] withIndent: 0];
//}
//
////-(NSString*) structureDescription {
////	NSMutableString* desc = [NSMutableString stringWithCapacity: 1000];
////	[self appendStructureDescriptionTo: desc withIndent: 0];
////	return desc;
////}
//
//-(NSString*) appendStructureDescriptionTo: (NSMutableString*) desc withIndent: (NSUInteger) indentLevel {
//	[desc appendFormat: @"\n"];
//	for (int i = 0; i < indentLevel; i++) {
//		[desc appendFormat: @"  "];
//	}
//	[desc appendFormat: @"%@", self];
//	for (CC3Node* child in children) {
//		[child appendStructureDescriptionTo: desc withIndent: indentLevel + 1];
//	}
//	return desc;
//}
//
//#pragma mark Matierial coloring
//
//-(BOOL) shouldUseLighting {
//	for (CC3Node* child in children) {
//		if (child.shouldUseLighting) {
//			return YES;
//		}
//	}
//	return NO;
//}
//
//-(void) setShouldUseLighting: (BOOL) useLighting {
//	for (CC3Node* child in children) {
//		child.shouldUseLighting = useLighting;
//	}
//}
//
//-(ccColor4F) ambientColor {
//	ccColor4F col = kCCC4FBlackTransparent;
//	int childCnt = 0;
//	if (children && (childCnt = children.count) > 0) {
//		GLfloat rSum, bSum, gSum, aSum;
//		rSum = bSum = gSum = aSum = 0.0f;
//		for (CC3Node* child in children) {
//			ccColor4F childColor = child.ambientColor;
//			rSum += childColor.r;
//			gSum += childColor.g;
//			bSum += childColor.b;
//			aSum += childColor.a;
//		}
//		col = CCC4FMake(rSum / childCnt, gSum / childCnt, bSum / childCnt, aSum / childCnt);
//	}
//	return col;
//}
//
//-(void) setAmbientColor: (ccColor4F) color {
//	for (CC3Node* child in children) {
//		child.ambientColor = color;
//	}
//}
//
//-(ccColor4F) diffuseColor {
//	ccColor4F col = kCCC4FBlackTransparent;
//	int childCnt = 0;
//	if (children && (childCnt = children.count) > 0) {
//		GLfloat rSum, bSum, gSum, aSum;
//		rSum = bSum = gSum = aSum = 0.0f;
//		for (CC3Node* child in children) {
//			ccColor4F childColor = child.diffuseColor;
//			rSum += childColor.r;
//			gSum += childColor.g;
//			bSum += childColor.b;
//			aSum += childColor.a;
//		}
//		col = CCC4FMake(rSum / childCnt, gSum / childCnt, bSum / childCnt, aSum / childCnt);
//	}
//	return col;
//}
//
//-(void) setDiffuseColor: (ccColor4F) color {
//	for (CC3Node* child in children) {
//		child.diffuseColor = color;
//	}
//}
//
//-(ccColor4F) specularColor {
//	ccColor4F col = kCCC4FBlackTransparent;
//	int childCnt = 0;
//	if (children && (childCnt = children.count) > 0) {
//		GLfloat rSum, bSum, gSum, aSum;
//		rSum = bSum = gSum = aSum = 0.0f;
//		for (CC3Node* child in children) {
//			ccColor4F childColor = child.specularColor;
//			rSum += childColor.r;
//			gSum += childColor.g;
//			bSum += childColor.b;
//			aSum += childColor.a;
//		}
//		col = CCC4FMake(rSum / childCnt, gSum / childCnt, bSum / childCnt, aSum / childCnt);
//	}
//	return col;
//}
//
//-(void) setSpecularColor: (ccColor4F) color {
//	for (CC3Node* child in children) {
//		child.specularColor = color;
//	}
//}
//
//-(ccColor4F) emissionColor {
//	ccColor4F col = kCCC4FBlackTransparent;
//	int childCnt = 0;
//	if (children && (childCnt = children.count) > 0) {
//		GLfloat rSum, bSum, gSum, aSum;
//		rSum = bSum = gSum = aSum = 0.0f;
//		for (CC3Node* child in children) {
//			ccColor4F childColor = child.emissionColor;
//			rSum += childColor.r;
//			gSum += childColor.g;
//			bSum += childColor.b;
//			aSum += childColor.a;
//		}
//		col = CCC4FMake(rSum / childCnt, gSum / childCnt, bSum / childCnt, aSum / childCnt);
//	}
//	return col;
//}
//
//-(void) setEmissionColor: (ccColor4F) color {
//	for (CC3Node* child in children) {
//		child.emissionColor = color;
//	}
//}
//
//-(CC3Vector) globalLightLocation {
//	for (CC3Node* child in children) {
//		CC3Vector cgll = child.globalLightLocation;
//		if ( !CC3VectorsAreEqual(cgll, kCC3VectorZero) ) {
//			return cgll;
//		}
//	}
//	return kCC3VectorZero;
//}
//
//-(void) setGlobalLightLocation: (CC3Vector) aDirection {
//	for (CC3Node* child in children) {
//		child.globalLightLocation = aDirection;
//	}
//}
//
//
//#pragma mark CCRGBAProtocol support
//
//-(ccColor3B) color {
//	ccColor3B col = ccBLACK;
//	int childCnt = 0;
//	if (children && (childCnt = children.count) > 0) {
//		GLuint rSum, bSum, gSum;
//		rSum = bSum = gSum = 0;
//		for (CC3Node* child in children) {
//			ccColor3B childColor = child.color;
//			rSum += childColor.r;
//			gSum += childColor.g;
//			bSum += childColor.b;
//		}
//		col = ccc3(rSum / childCnt, gSum / childCnt, bSum / childCnt);
//	}
//	return col;
//}
//
//-(void) setColor: (ccColor3B) color {
//	for (CC3Node* child in children) {
//		child.color = color;
//	}
//}
//
//-(GLubyte) opacity {
//	GLubyte opc = 0;
//	int childCnt = 0;
//	if (children && (childCnt = children.count) > 0) {
//		GLuint oSum = 0;
//		for (CC3Node* child in children) {
//			oSum += child.opacity;
//		}
//		opc = oSum / childCnt;
//	}
//	return opc;
//}
//
//-(void) setOpacity: (GLubyte) opacity {
//	for (CC3Node* child in children) {
//		child.opacity = opacity;
//	}
//}
//
//-(BOOL) isOpaque {
//	for (CC3Node* child in children) {
//		if(!child.isOpaque) {
//			return NO;
//		}
//	}
//	return YES;
//}
//
//-(void) setIsOpaque: (BOOL) opaque {
//	for (CC3Node* child in children) {
//		child.isOpaque = opaque;
//	}
//}
//
//
//#pragma mark Allocation and initialization
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		transformMatrixInverted = nil;
//		globalRotationMatrix = nil;
//		rotator = [[[self rotatorClass] rotator] retain];
//		boundingVolume = nil;
//		location = kCC3VectorZero;
//		globalLocation = kCC3VectorZero;
//		projectedLocation = kCC3VectorZero;
//		scale = kCC3VectorUnitCube;
//		globalScale = kCC3VectorUnitCube;
//		isTransformDirty = NO;			// everything starts out at identity
//		isTouchEnabled = NO;
//		shouldInheritTouchability = YES;
//		isAnimationEnabled = YES;
//		visible = YES;
//		isRunning = NO;
//		shouldAutoremoveWhenEmpty = NO;
//		self.transformMatrix = [CC3GLMatrix identity];		// Has side effects...so do last (transformMatrixInverted is built in some subclasses)
//	}
//	return self;
//}
//
//+(id) node {
//	return [[[self alloc] init] autorelease];
//}
//
//+(id) nodeWithTag: (GLuint) aTag {
//	return [[[self alloc] initWithTag: aTag] autorelease];
//}
//
//+(id) nodeWithName: (NSString*) aName {
//	return [[[self alloc] initWithName: aName] autorelease];
//}
//
//+(id) nodeWithTag: (GLuint) aTag withName: (NSString*) aName {
//	return [[[self alloc] initWithTag: aTag withName: aName] autorelease];
//}
//
///**
// * Populates this instance with content copied from the specified other node.
// *
// * The population of this node from the content of the other node effects a deep copy.
// * For any content that is held by reference (eg- objects), and subject to future
// * modification, a copy is created, so that both this instance and the other instance can
// * be treated independently.
// * 
// * Child nodes are not copied in this method. Once this node has been populated with
// * configuration content by this method, invoke the copyChildrenFrom: method to copy
// * the child nodes from the other node.
// * 
// * Subclasses that extend copying should extend this method, and honour the deep copy
// * design pattern, making exceptions only for content that is both large and not subject
// * to modifications, such as mesh data.
// */
//-(void) populateFrom: (CC3Node*) another {
//	[super populateFrom: another];
//
//	// Bypass setter method to avoid side effects.
//	[transformMatrix release];
//	transformMatrix = [another.transformMatrix copy];		// retained
//
//	isTransformInvertedDirty = YES;							// create or rebuild lazily
//	isGlobalRotationDirty = YES;							// create or rebuild lazily
//
//	[rotator release];
//	rotator = [another.rotator copy];						// retained
//	
//	[boundingVolume release];
//	boundingVolume = [another.boundingVolume copy];			// retained
//	boundingVolume.node = self;
//
//	[animation release];
//	animation = [another.animation retain];					// retained...not copied
//
//	location = another.location;
//	globalLocation = another.globalLocation;
//	projectedLocation = another.projectedLocation;
//	scale = another.scale;
//	globalScale = another.globalScale;
//	isTransformDirty = another.isTransformDirty;
//	isTouchEnabled = another.isTouchEnabled;
//	shouldInheritTouchability = another.shouldInheritTouchability;
//	isAnimationEnabled = another.isAnimationEnabled;
//	visible = another.visible;
//	isRunning = another.isRunning;
//	shouldAutoremoveWhenEmpty = another.shouldAutoremoveWhenEmpty;
//	self.shouldDrawDescriptor = another.shouldDrawDescriptor;		// May create a child node
//	self.shouldDrawWireframeBox = another.shouldDrawWireframeBox;	// May create a child node
//}
//
//// Protected properties for copying
//-(BOOL) isTransformDirty { return isTransformDirty; }
//-(CC3Rotator*) rotator { return rotator; }
//
///**
// * Copying of children is performed here instead of in populateFrom:
// * so that subclasses will be completely configured before children are added.
// * Subclasses that extend copying should not override this method,
// * but should override the populateFrom: method instead.
// */
//-(id) copyWithZone: (NSZone*) zone withName: (NSString*) aName asClass: (Class) aClass {
//	CC3Node* aCopy = (CC3Node*)[super copyWithZone: zone withName: aName asClass: aClass];
//	[aCopy copyChildrenFrom: self];
//	return aCopy;
//}
//
///**
// * Adds copies of the child nodes of the other node to this node.
// * Only the children that have the shouldIncludeInDeepCopy property
// * set to YES are copied.
// */
//-(void) copyChildrenFrom: (CC3Node*) another {
//	[self removeAllChildren];
//	NSArray* otherKids = another.children;
//	if (otherKids) {
//		for (CC3Node* n in otherKids) {
//			if (n.shouldIncludeInDeepCopy) {
//				[self addChild: [n copyAutoreleased]];	// retained by collection
//			}
//		}
//	}
//}
//
//// Implementations to keep compiler happy so this method can be included in interface for documentation.
//-(id) copy { return [super copy]; }
//-(id) copyWithName: (NSString*) aName { return [super copyWithName: aName]; }
//
//-(void) createGLBuffers {
//	for (CC3Node* child in children) {
//		[child createGLBuffers];
//	}
//}
//
//-(void) deleteGLBuffers {
//	for (CC3Node* child in children) {
//		[child deleteGLBuffers];
//	}
//}
//
//-(void) releaseRedundantData {
//	for (CC3Node* child in children) {
//		[child releaseRedundantData];
//	}
//}
//
//-(void) retainVertexLocations {
//	for (CC3Node* child in children) {
//		[child retainVertexLocations];
//	}
//}
//
//-(void) retainVertexNormals {
//	for (CC3Node* child in children) {
//		[child retainVertexNormals];
//	}
//}
//
//-(void) retainVertexColors {
//	for (CC3Node* child in children) {
//		[child retainVertexColors];
//	}
//}
//
//-(void) retainVertexTextureCoordinates {
//	for (CC3Node* child in children) {
//		[child retainVertexTextureCoordinates];
//	}
//}
//
//-(void) retainVertexIndices {
//	for (CC3Node* child in children) {
//		[child retainVertexIndices];
//	}
//}
//
//-(void) doNotBufferVertexLocations {
//	for (CC3Node* child in children) {
//		[child doNotBufferVertexLocations];
//	}
//}
//
//-(void) doNotBufferVertexNormals {
//	for (CC3Node* child in children) {
//		[child doNotBufferVertexNormals];
//	}
//}
//
//-(void) doNotBufferVertexColors {
//	for (CC3Node* child in children) {
//		[child doNotBufferVertexColors];
//	}
//}
//
//-(void) doNotBufferVertexTextureCoordinates {
//	for (CC3Node* child in children) {
//		[child doNotBufferVertexTextureCoordinates];
//	}
//}
//
//-(void) doNotBufferVertexIndices {
//	for (CC3Node* child in children) {
//		[child doNotBufferVertexIndices];
//	}
//}
//
//
//#pragma mark Texture alignment
//
//-(void) alignTextures {
//	for (CC3Node* child in children) {
//		[child alignTextures];
//	}
//}
//
//-(void) alignInvertedTextures {
//	for (CC3Node* child in children) {
//		[child alignInvertedTextures];
//	}
//}
//
///**
// * Rotation tracking for each node is handled by a encapsulated instance of CC3Rotator.
// * This method returns the subclass of CC3Rotator that will be instantiated and used by
// * instances of this node class. The default is CC3Rotator, but subclasses my override
// * to establish other rotational tracking functionality.
// */
//-(id) rotatorClass {
//	return [CC3Rotator class];
//}
//
//#pragma mark Tag allocation
//
//// Class variable tracking the most recent tag value assigned for CC3Nodes.
//// This class variable is automatically incremented whenever the method nextTag is called.
//static GLuint lastAssignedNodeTag;
//
//-(GLuint) nextTag {
//	return ++lastAssignedNodeTag;
//}
//
//+(void) resetTagAllocation {
//	lastAssignedNodeTag = 0;
//}
//
//
//#pragma mark Type testing
//
//-(BOOL) hasLocalContent {
//	return NO;
//}
//
//-(BOOL) isMeshNode {
//	return NO;
//}
//
//-(BOOL) visible {
//	return visible && (!parent || parent.visible);
//}
//
//
//#pragma mark Updating
//
//// Deprecated legacy method - supported for backwards compatibility
//-(void) update: (ccTime)dt {}
//
//// Deprecated legacy method - supported for backwards compatibility
//-(void) updateBeforeChildren: (CC3NodeUpdatingVisitor*) visitor {}
//
//// Deprecated legacy method - supported for backwards compatibility
//-(void) updateAfterChildren: (CC3NodeUpdatingVisitor*) visitor {}
//
//// Default invokes legacy updateBeforeChildren: and update: methods, for backwards compatibility.
//-(void) updateBeforeTransform: (CC3NodeUpdatingVisitor*) visitor {
//	[self updateBeforeChildren: visitor];
//	[self update: visitor.deltaTime];
//}
//
//// Default invokes legacy updateAfterChildren: method, for backwards compatibility.
//-(void) updateAfterTransform: (CC3NodeUpdatingVisitor*) visitor {
//	[self updateAfterChildren: visitor];
//}
//
//
//#pragma mark Transformations
//
//-(void) setTransformMatrix: (CC3GLMatrix*) aCC3GLMatrix {
//	if (transformMatrix != aCC3GLMatrix) {
//		[transformMatrix release];
//		transformMatrix = [aCC3GLMatrix retain];
//		[self updateGlobalOrientation];
//		[self transformMatrixChanged];
//	}
//}
//
///** Marks the node's transformMatrix as requiring a recalculation. */
//-(void) markTransformDirty {
//	isTransformDirty = YES;
//}
//
//-(void) updateTransformMatrix {
//	CC3NodeTransformingVisitor* visitor = [[self transformVisitorClass] visitor];
//	visitor.shouldVisitChildren = NO;
//	[visitor visit: self];
//}
//
//-(void) updateTransformMatrices {
//	[[[self transformVisitorClass] visitor] visit: self];
//}
//
///**
// * Returns the class of visitor that will be instantiated in the updateWorld: method,
// * and passed to the updateTransformMatrices method when the transformation matrices
// * of the nodes are being rebuilt.
// *
// * The returned class must be a subclass of CC3NodeTransformingVisitor. This implementation
// * returns CC3NodeTransformingVisitor. Subclasses may override to customized the behaviour
// * of the update visits.
// */
//-(id) transformVisitorClass {
//	return [CC3NodeTransformingVisitor class];
//}
//
//-(void) buildTransformMatrixWithVisitor: (CC3NodeTransformingVisitor*) visitor {
//	CC3GLMatrix* pMtx = [visitor parentTansformMatrixFor: self];
//	if (pMtx) {
//		[transformMatrix populateFrom: pMtx];
//	} else {
//		[transformMatrix populateIdentity];
//	}
//	[self applyLocalTransforms];
//	[self transformMatrixChanged];
//}
//
///**
// * Template method that applies the local location, rotation and scale properties to
// * the transform matrix. Subclasses may override to enhance or modify this behaviour.
// */
//-(void) applyLocalTransforms {
//	[self applyTranslation];
//	[self applyRotation];
//	[self applyScaling];
//}
//
///** Template method that applies the local location property to the transform matrix. */
//-(void) applyTranslation {
//	[transformMatrix translateBy: location];
//	[self updateGlobalLocation];
//	LogTrace(@"%@ translated to %@, globally %@ %@", self, NSStringFromCC3Vector(location),
//			 NSStringFromCC3Vector(globalLocation), transformMatrix);
//}
//
///** Template method that applies the local rotation property to the transform matrix. */
//-(void) applyRotation {
//	[rotator applyRotationTo: transformMatrix];
//	[self updateGlobalRotation];
//	LogTrace(@"%@ rotated to %@ %@", self, NSStringFromCC3Vector(rotator.rotation), transformMatrix);
//}
//
///** Template method that applies the local scale property to the transform matrix. */
//-(void) applyScaling {
//	[transformMatrix scaleBy: scale];
//	[self updateGlobalScale];
//	LogTrace(@"%@ scaled to %@, globally %@ %@", self, NSStringFromCC3Vector(scale),
//			 NSStringFromCC3Vector(globalScale), transformMatrix);
//}
//
///**
// * Template method that is invoked automatically whenever the transform matrix of this node
// * is changed. Updates the bounding volume of this node, and marks the globalRotationMatrix
// * and transformInvertedMatrix as dirty so they will be lazily rebuilt.
// */
//-(void) transformMatrixChanged {
//	[self updateBoundingVolume];
//	isTransformDirty = NO;
//	isTransformInvertedDirty = YES;
//}
//
///**
// * Template method that updates the global orientation properties
// * (globalLocation, globalRotation & globalScale).
// */
//-(void) updateGlobalOrientation {
//	[self updateGlobalLocation];
//	[self updateGlobalRotation];
//	[self updateGlobalScale];
//}
//
///** Template method to update the globalLocation property. */
//-(void) updateGlobalLocation {
//	globalLocation = [transformMatrix transformLocation: kCC3VectorZero];
//}
//
///** Template method to update the globalRotation property. */
//-(void) updateGlobalRotation {
//	isGlobalRotationDirty = YES;
//}
//
///** Template method to update the globalScale property. */
//-(void) updateGlobalScale {
//	globalScale = parent ? CC3VectorScale(parent.globalScale, scale) : scale;
//}
//
///**
// * Returns the inverse of the transformMatrix.
// *
// * Since this inverse matrix is not commonly used, and is often expensive to compute,
// * it is only calculated when the transformMatrix has changed, and then only on demand.
// * When the transformMatrix is marked as dirty, the tansformMatrixInverted is marked
// * as dirty as well. It is then recalculated the next time this property is accessed,
// * and is cached until it is marked dirty again.
// *
// * The calculation of the inverse is optimized where possible. If the transformMatrix
// * is rigid, as indicated by the isTransformRigid property, the inversion calculation
// * is optimized to a rotational transposition and reverse translation, which is many
// * times faster than a full matrix inversion calculation.
// */
//-(CC3GLMatrix*) transformMatrixInverted {
//	if (!transformMatrixInverted) {
//		transformMatrixInverted = [[CC3GLMatrix matrix] retain];
//		isTransformInvertedDirty = YES;
//	}
//	if (isTransformInvertedDirty) {
//		[transformMatrixInverted populateFrom: transformMatrix];
//		
//		// If the transform is rigid (only rotation & translation), use faster inversion.
//		// It would be better to move this test to the matrix itself, but there is no
//		// known quick and accurate test to tell if a matrix represents a rigid transform.
//		if (self.isTransformRigid) {
//			[transformMatrixInverted invertRigid];
//		} else {
//			[transformMatrixInverted invertAffine];
//		}
//		isTransformInvertedDirty = NO;
//
//		LogTrace(@"%@ transform %@ inverted %@to %@", self, transformMatrix,
//				 (self.isTransformRigid ? @"rigidly " : @""), transformMatrixInverted);
//	}
//	return transformMatrixInverted;
//}
//
///**
// * Returns a matrix representing all of the rotations that make up this node,
// * including ancestor nodes.
// *
// * Since this matrix is not commonly used, and is expensive to compute, it is only
// * calculated when the transformMatrix has changed, and then only on demand.
// * When the transformMatrix is marked as dirty, the globalRotationMatrix is marked
// * as dirty as well. It is then recalculated the next time this property is accessed,
// * and is cached until it is marked dirty again.
// */
//-(CC3GLMatrix*) globalRotationMatrix {
//	if (!globalRotationMatrix) {
//		globalRotationMatrix = [[CC3GLMatrix matrix] retain];
//		isGlobalRotationDirty = YES;
//	}
//	if (isGlobalRotationDirty) {
//		if (parent) {
//			[globalRotationMatrix populateFrom: parent.globalRotationMatrix];
//			[globalRotationMatrix multiplyByMatrix: rotator.rotationMatrix];
//		} else {
//			[globalRotationMatrix populateFrom: rotator.rotationMatrix];
//		}
//		isGlobalRotationDirty = NO;
//	}
//	return globalRotationMatrix;
//}
//
///** Template method that updates the bounding volume. */
//-(void) updateBoundingVolume {
//	[boundingVolume update];
//}
//
//
//#pragma mark Drawing
//
//-(void) drawWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	if (self.visible && self.hasLocalContent && [self doesIntersectFrustum: visitor.frustum]) {
//		[self transformAndDrawWithVisitor: visitor];
//	}
//}
//
///**
// * Returns whether the local content of this node intersects the given frustum. If this node
// * has a boundingVolume, it delegates to it, otherwise, it simply returns YES.
// * Subclasses may override to change this standard behaviour. 
// */
//-(BOOL) doesIntersectFrustum: (CC3Frustum*) aFrustum {
//	if (boundingVolume && aFrustum) {
//		BOOL intersects = [boundingVolume doesIntersectFrustum: aFrustum];
//		LogTrace(@"%@ bounded by %@ %@\n%@", self, boundingVolume,
//				 (intersects ? @"intersects" : @"does not intersect"), aFrustum);
//
//		// Uncomment and change name to verify culling:
////		if (!intersects && ([self.name isEqualToString: @"MyNodeName"])) {
////			LogDebug(@"%@ does not intersect\n%@", self, aFrustum);
////		}
//		return intersects;
//	}
//	return YES;
//}
//
///** Template method that applies this node's transform matrix to the GL matrix stack and draws this node. */
//-(void) transformAndDrawWithVisitor: (CC3NodeDrawingVisitor*) visitor {
//	LogTrace(@"Drawing %@", self);
//	CC3OpenGLES11MatrixStack* gles11MatrixStack = [CC3OpenGLES11Engine engine].matrices.modelview;
//
//	[gles11MatrixStack push];
//
//	LogTrace(@"%@ applying transform matrix: %@", self, transformMatrix);
//	[gles11MatrixStack multiply: transformMatrix.glMatrix];
//
//	[visitor drawLocalContentOf: self];
//
//	[gles11MatrixStack pop];
//}
//
///**
// * Draws the raw, untransformed local content of this node. This implementation does nothing.
// * Subclasses with drawable local content will override as appropriate to draw their content.
// *
// * As described in the class documentation, in keeping with best practices, drawing and frame
// * rendering should be kept separate from updating the model state. Therefore, when overriding
// * this method in a subclass, do not update any model state. This method should perform only
// * frame rendering operations.
// */
//-(void) drawLocalContentWithVisitor: (CC3NodeDrawingVisitor*) visitor {}
//
//-(void) checkDrawingOrder {
//	for (CC3Node* child in children) {
//		[child checkDrawingOrder];
//	}
//}
//
//
//#pragma mark Node structural hierarchy
//
///**
// * When assigned to a new parent, ensure that the transform will be recalculated,
// * since it changes this child's overall transform.
// */
//-(void) setParent: (CC3Node *) aNode {
//	parent = aNode;
//	[self markTransformDirty];
//}
//
//-(CC3Node*) rootAncestor {
//	return parent ? parent.rootAncestor : self;
//}
//
///** Adds a child node and invokes didAddDescendant: so action can be taken by subclasses. */
//-(void) addChild: (CC3Node*) aNode {
//	// Don't add if child is nil or is already a child of this node
//	NSAssert(aNode, @"Child CC3Node cannot be nil");
//	if(aNode.parent == self) return;
//
//	[aNode remove];				// Remove node from any existing parent
//	if(!children) {
//		children = [[NSMutableArray array] retain];
//	}
//	[children addObject: aNode];
//	aNode.parent = self;
//	aNode.isRunning = self.isRunning;
//	[self didAddDescendant: aNode];
//	LogTrace(@"After adding %@, %@ now has children: %@", aNode, self, children);
//}
//
///**
// * To transform location and rotation, we invert the matrix of this node,
// * and multiply it by the matrix of the child node. The incoming child's
// * matrix is in global form. We want a local form that will provide the
// * local location and rotation. We can then extract local location,
// * rotation, and scale from the local matrix.
// *
// * Mathematically, if Mcg is the global matrix of the child node, Mpg is the
// * matrix of this parent, and Mcl is the desired local matrix, we have:
// *     Normally: Mcg = Mpg.Mcl
// * Multiplying both sides by  Mpg(-1), the inverse of the parent's matrix:
// *     Mpg(-1).Mcg = Mpg(-1).Mpg.Mcl
// *     Mcl = Mpg(-1).Mcg
// */
//-(void) addAndLocalizeChild: (CC3Node*) aNode {
//	CC3GLMatrix* g2LMtx;		// Global to local transformation matrix
//	
//	// Since this calculation depends both the parent and child transformMatrixes,
//	// make sure they are up to date.
//	[self updateTransformMatrix];
//	[aNode updateTransformMatrix];
//	
//	// Localize the child node's location by finding the right local matrix,
//	// and then translating the child node's local origin by the resulting matrix.
//	// This is what the location property does. It instructs the local matrix
//	// to move the node's origin. By transforming the origin, we determine what
//	// that location property needs to be.
//	g2LMtx = [self.transformMatrixInverted copyAutoreleased];
//	[g2LMtx multiplyByMatrix: aNode.transformMatrix];
//	aNode.location = [g2LMtx transformLocation: kCC3VectorZero];
//
//	// Localize the child node's rotation by finding the right rotation matrix.
//	// For rotation, we use the globalRotationMatrix, which is free of scale
//	// and translation content. Otherwise it would be impossible to extract
//	// the local rotation from an arbitrarily scaled and translated matrix.
//	g2LMtx = [self.globalRotationMatrix copyAutoreleased];
//	[g2LMtx invertRigid];		// Only contains rotation...so it's rigid.
//	[g2LMtx multiplyByMatrix: aNode.globalRotationMatrix];
//	aNode.rotation = [g2LMtx extractRotation];
//
//	// Scale cannot readily be extracted from the inverted and multiplied matrix,
//	// but we can get it by scaling the node's scale down by the globalScale
//	// of this parent, so that when they are recombined, the original globalScale
//	// of the child node.
//	aNode.scale = CC3VectorScale(aNode.globalScale, CC3VectorInvert(self.globalScale));
//
//	[self addChild:aNode];		// Finally, add the child node to this parent
//}
//
///**
// * Removes a child node and invokes didRemoveDescendant: so action can be taken by subclasses.
// * First locates the object to make sure it is in the child node collection, and only performs
// * the removal and related actions if the specified node really is a child of this node.
// * Also removes this node if the shouldAutoremoveWhenEmpty property is YES, and the last
// * child has just been removed.
// */
//-(void) removeChild: (CC3Node*) aNode {
//	if (children && aNode) {
//		NSUInteger indx = [children indexOfObjectIdenticalTo: aNode];
//		if (indx != NSNotFound) {
//
//			// If the children collection is the only thing referencing the child node, the
//			// child node will be deallocated as soon as it is removed, and will be invalid
//			// when passed to the didRemoveDescendant: method, or to other activities that
//			// it may be subject to in the processing loop. To avoid problems, retain it for
//			// the duration of this processing loop, so that it will still be valid until
//			// we're done with it.
//			[[aNode retain] autorelease];
//
//			aNode.parent = nil;
//			[children removeObjectAtIndex: indx];
//			if (children.count == 0) {
//				[children release];
//				children = nil;
//			}
//			aNode.isRunning = NO;
//			[self didRemoveDescendant: aNode];
//		}
//		LogTrace(@"After removing %@, %@ now has children: %@", aNode, self, children);
//		
//		// If the last child has been removed, and this instance should autoremove when
//		// that occurs, remove this node from the hierarchy as well. This must be performed
//		// after everything else is done, particularly only after the didRemoveDescendant:
//		// has been invoked so that that notification can propagate up the node hierarchy.
//		if (!children && shouldAutoremoveWhenEmpty) {
//			[self remove];
//		}
//	}
//}
//
//-(void) removeAllChildren {
//	NSArray* myKids = [children copy];
//	for (CC3Node* child in myKids) {
//		[self removeChild: child];
//	}
//	[myKids release];
//}
//
//-(void) remove {
//	[parent removeChild: self];
//}
//
//-(BOOL) isDescendantOf: (CC3Node*) aNode {
//	return parent ? (parent == aNode || [parent isDescendantOf: aNode]) : NO;
//}
//
///**
// * Invoked automatically when a node is added as a child somewhere in the descendant structural
// * hierarchy of this node. The method is not only invoked on the immediate parent of the newly
// * added node, but is actually invoked on all ancestors as well (parents of the parent).
// * This default implementation simply passes the notification up the parental ancestor chain.
// * Subclasses may override to take a specific interest in which nodes are being added below them.
// */
//-(void) didAddDescendant: (CC3Node*) aNode {
//	[parent didAddDescendant: (CC3Node*) aNode];
//}
//
///**
// * Invoked automatically when a node is removed as a child somewhere in the descendant structural
// * hierarchy of this node. The method is not only invoked on the immediate parent of the removed
// * node, but is actually invoked on all ancestors as well (parents of the parent).
// * This default implementation simply passes the notification up the parental ancestor chain.
// * Subclasses may override to take a specific interest in which nodes are being removed below them.
// */
//-(void) didRemoveDescendant: (CC3Node*) aNode {
//	[parent didRemoveDescendant: (CC3Node*) aNode];
//}
//
///**
// * Invoked automatically when a property was modified on a descendant node that potentially
// * affects its drawing order, relative to other nodes. This default implementation simply
// * passes the notification up the parental ancestor chain. Subclasses may override to take
// * a specific interest in which nodes need resorting below them.
// */
//-(void) descendantDidModifySequencingCriteria: (CC3Node*) aNode {
//	[parent descendantDidModifySequencingCriteria: aNode];
//}
//
//-(CC3Node*) getNodeNamed: (NSString*) aName {
//	if ([name isEqual: aName] || (!name && !aName)) {	// my name equal or both nil
//		return self;
//	}
//	for (CC3Node* child in children) {
//		CC3Node* childResult = [child getNodeNamed: aName];
//		if (childResult) {
//			return childResult;
//		}
//	}
//	return nil;
//}
//
//-(CC3Node*) getNodeTagged: (GLuint) aTag {
//	if (tag == aTag) {
//		return self;
//	}
//	for (CC3Node* child in children) {
//		CC3Node* childResult = [child getNodeTagged: aTag];
//		if (childResult) {
//			return childResult;
//		}
//	}
//	return nil;
//}
//
//-(NSArray*) flatten {
//	NSMutableArray* allNodes = [NSMutableArray array];
//	[self flattenInto: allNodes];
//	return allNodes;
//}
//
//-(void) flattenInto: (NSMutableArray*) anArray {
//	[anArray addObject: self];
//	for (CC3Node* child in children) {
//		[child flattenInto: anArray];
//	}
//}
//
//
//#pragma mark CC3Node Actions
//
//-(CCAction*) runAction:(CCAction*) action {
//	NSAssert( action != nil, @"Argument must be non-nil");
//	[[CCActionManager sharedManager] addAction: action target: self paused: !isRunning];
//	return action;
//}
//
//-(void) stopAllActions {
//	[[CCActionManager sharedManager] removeAllActionsFromTarget: self];
//}
//
//-(void) stopAction: (CCAction*) action {
//	[[CCActionManager sharedManager] removeAction: action];
//}
//
//-(void) stopActionByTag:(int)aTag {
//	NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
//	[[CCActionManager sharedManager] removeActionByTag: aTag target: self];
//}
//
//-(CCAction*) getActionByTag:(int) aTag {
//	NSAssert( aTag != kCCActionTagInvalid, @"Invalid tag");
//	return [[CCActionManager sharedManager] getActionByTag: aTag target: self];
//}
//
//-(int) numberOfRunningActions {
//	return [[CCActionManager sharedManager] numberOfRunningActionsInTarget: self];
//}
//
//- (void) resumeActions {
//	[[CCActionManager sharedManager] resumeTarget: self];
//}
//
//- (void) pauseActions {
//	[[CCActionManager sharedManager] pauseTarget: self];
//}
//
//- (void)cleanup {
//	[self stopAllActions];
//	for (CC3Node* child in children) {
//		[child cleanup];
//	}
//}
//
//
//#pragma mark Touch handling
//
//-(BOOL) isTouchable {
//	return isTouchEnabled || ((parent && shouldInheritTouchability) ? parent.isTouchable : NO);
//}
//
//-(CC3Node*) touchableNode {
//	return isTouchEnabled ? self : (parent ? parent.touchableNode : nil);
//}
//
//-(void) touchEnableAll {
//	isTouchEnabled = YES;
//	for (CC3Node* child in children) {
//		[child touchEnableAll];
//	}
//}
//
//-(void) touchDisableAll {
//	isTouchEnabled = NO;
//	for (CC3Node* child in children) {
//		[child touchDisableAll];
//	}
//}
//
//
//#pragma mark Animation
//
//-(BOOL) containsAnimation {
//	if (animation) {
//		return YES;
//	}
//	for (CC3Node* child in children) {
//		if (child.containsAnimation) {
//			return YES;
//		}
//	}
//	return NO;
//}
//
//-(void) enableAnimation {
//	isAnimationEnabled = YES;
//}
//
//-(void) disableAnimation {
//	isAnimationEnabled = NO;
//}
//
//-(void) enableAllAnimation {
//	[self enableAnimation];
//	for (CC3Node* child in children) {
//		[child enableAllAnimation];
//	}
//}
//
//-(void) disableAllAnimation {
//	[self disableAnimation];
//	for (CC3Node* child in children) {
//		[child disableAllAnimation];
//	}
//}
//
//-(void) establishAnimationFrameAt: (ccTime) t {
//	if (animation && isAnimationEnabled) {
//		LogTrace(@"%@ animating frame at %.3f ms", self, t);
//		[animation establishFrameAt: t forNode: self];
//	}
//	for (CC3Node* child in children) {
//		[child establishAnimationFrameAt: t];
//	}
//}
//
//
//#pragma mark Wireframe box and descriptor
//
///** Suffix used to name the descriptor child node. */
//#define kDescriptorSuffix @"DESC"
//
///**
// * The name to use when creating or retrieving the descriptor child node of this node.
// * For uniqueness, includes the tag of this node in case this node has no name.
// */
//-(NSString*) descriptorName {
//	return [NSString stringWithFormat: @"%@-%u-%@", self.name, self.tag, kDescriptorSuffix];
//}
//
//-(CC3NodeDescriptor*) descriptorNode {
//	return (CC3NodeDescriptor*)[self getNodeNamed: [self descriptorName]];
//}
//
//-(ccColor3B) initialDescriptorColor {
//	return CCC3BFromCCC4F(self.initialWireframeBoxColor);
//}
//
//-(BOOL) shouldDrawDescriptor {
//	return (self.descriptorNode != nil);
//}
//
//-(void) setShouldDrawDescriptor: (BOOL) shouldDraw {
//	
//	// Fetch the descriptor node from the child nodes.
//	CC3NodeDescriptor* dn = self.descriptorNode;
//	
//	// If the descriptor node exists, but should not, remove it
//	if (dn && !shouldDraw) {
//		[dn remove];
//	}
//	
//	// If there is no descriptor node, but there should be, add it by creating a
//	// CC3NodeDescriptor from the description property and add it as a child of this node.
//	if(!dn && shouldDraw) {
//		CCLabelTTF* dnLabel = [CCLabelTTF labelWithString: self.description
//												 fontName: @"Arial"
//												 fontSize: [[self class] descriptorFontSize]];
//		dn = [CC3NodeDescriptor nodeWithName: [self descriptorName] withBillboard: dnLabel];
//		dn.color = self.initialDescriptorColor;
//		[self addChild: dn];
//	}
//}
//
//-(BOOL) shouldDrawAllDescriptors {
//	if (!self.shouldDrawDescriptor) {
//		return NO;
//	}
//	for (CC3Node* child in children) {
//		if (!child.shouldDrawAllDescriptors) {
//			return NO;
//		}
//	}
//	return YES;
//}
//
//-(void) setShouldDrawAllDescriptors: (BOOL) shouldDraw {
//	self.shouldDrawDescriptor = shouldDraw;
//	for (CC3Node* child in children) {
//		child.shouldDrawAllDescriptors = shouldDraw;
//	}
//}
//
//// Initial font size for any new descriptors
//static CGFloat descriptorFontSize = 14.0;
//
//+(CGFloat) descriptorFontSize {
//	return descriptorFontSize;
//}
//
//+(void) setDescriptorFontSize: (CGFloat) fontSize {
//	descriptorFontSize = fontSize;
//}
//
//
///** Suffix used to name the wireframe child node. */
//#define kWireframeBoxSuffix @"WFB"
//
///**
// * The name to use when creating or retrieving the wireframe child node of this node.
// * For uniqueness, includes the tag of this node in case this node has no name.
// */
//-(NSString*) wireframeBoxName {
//	return [NSString stringWithFormat: @"%@-%u-%@", self.name, self.tag, kWireframeBoxSuffix];
//}
//
//-(CC3WireframeBoundingBoxNode*) wireframeBoxNode {
//	return (CC3WireframeBoundingBoxNode*)[self getNodeNamed: [self wireframeBoxName]];
//}
//
//-(BOOL) shouldDrawWireframeBox {
//	return (self.wireframeBoxNode != nil);
//}
//
//-(void) setShouldDrawWireframeBox: (BOOL) shouldDraw {
//	
//	// Fetch the wireframe node from the child nodes.
//	CC3WireframeBoundingBoxNode* wf = self.wireframeBoxNode;
//	
//	// If the wireframe exists, but should not, remove it
//	if (wf && !shouldDraw) {
//		[wf remove];
//	}
//	
//	// If there is no wireframe, but there should be, add it by creating a
//	// CC3WireframeBoundingBoxNode from the boundingBox property and add it as a
//	// child of this node. If the bounding box is null, don't create a wireframe.
//	// The bounding box is set to update its vertices from the bounding box of
//	// this node on each update pass to allow the wireframe to grow and shrink
//	// along with the bounding box of this node and its descendants
//	if(!wf && shouldDraw) {
//		CC3BoundingBox gbb = self.boundingBox;
//		if ( !CC3BoundingBoxIsNull(gbb) ) {
//			wf = [CC3WireframeBoundingBoxNode nodeWithName: [self wireframeBoxName]];
//			[wf populateAsWireBox: gbb];
//			wf.pureColor = self.initialWireframeBoxColor;
//			wf.shouldAlwaysMeasureParentBoundingBox = YES;
//			[self addChild: wf];
//		}
//	}
//}
//
///** If default is transparent black, use the color of the node. */
//-(ccColor4F) initialWireframeBoxColor {
//	ccColor4F defaultColor = [[self class] wireframeBoxColor];
//	return CCC4FAreEqual(defaultColor, kCCC4FBlackTransparent)
//				? ccc4FFromccc3B(self.color) 
//				: defaultColor;
//}
//
//// The default color to use when drawing the wireframes
//static ccColor4F wireframeBoxColor = { 1.0, 1.0, 0.0, 1.0 };	// kCCC4FYellow
//
//+(ccColor4F) wireframeBoxColor {
//	return wireframeBoxColor;
//}
//
//+(void) setWireframeBoxColor: (ccColor4F) aColor {
//	wireframeBoxColor = aColor;
//}
//
//-(BOOL) shouldDrawAllWireframeBoxes {
//	if (!self.shouldDrawWireframeBox) {
//		return NO;
//	}
//	for (CC3Node* child in children) {
//		if (!child.shouldDrawAllWireframeBoxes) {
//			return NO;
//		}
//	}
//	return YES;
//}
//
//-(void) setShouldDrawAllWireframeBoxes: (BOOL) shouldDraw {
//	self.shouldDrawWireframeBox = shouldDraw;
//	for (CC3Node* child in children) {
//		child.shouldDrawAllWireframeBoxes = shouldDraw;
//	}
//}
//
//-(BOOL) shouldDrawAllLocalContentWireframeBoxes {
//	for (CC3Node* child in children) {
//		if (!child.shouldDrawAllLocalContentWireframeBoxes) {
//			return NO;
//		}
//	}
//	return YES;
//}
//
//-(void) setShouldDrawAllLocalContentWireframeBoxes: (BOOL) shouldDraw {
//	for (CC3Node* child in children) {
//		child.shouldDrawAllLocalContentWireframeBoxes = shouldDraw;
//	}
//}
//
//@end


#pragma mark -
#pragma mark CC3LocalContentNode

CC3LocalContentNode::CC3LocalContentNode()
{
}

CC3LocalContentNode::~CC3LocalContentNode()
{
}

CC3BoundingBox CC3LocalContentNode::getLocalContentBoundingBox()
{
	return kCC3BoundingBoxNull;
}

CC3BoundingBox CC3LocalContentNode::getGlobalLocalContentBoundingBox()
{
	// If the global bounding box is null, rebuild it, otherwise return it.
	if (CC3BoundingBoxIsNull(globalLocalContentBoundingBox)) 
	{
		
		// Get the mesh bounding box (in local coords). If it's null, return null.
		CC3BoundingBox mbb = getLocalContentBoundingBox();
		if (CC3BoundingBoxIsNull(mbb)) 
		{
			return kCC3BoundingBoxNull;
		}
		
		// The eight vertices of the transformed mesh bounding box
		CC3Vector gbbVertices[8];
		CC3GLMatrix* tMtx = getTransformMatrix();
		
		// Get the corners of the local bounding box
		CC3Vector bbMin = mbb.minimum;
		CC3Vector bbMax = mbb.maximum;
		
		// Construct all 8 corner vertices of the local bounding box and transform each
		// to global coordinates. The result is an oriented-bounding-box.
		gbbVertices[0] = tMtx->transformLocation(cc3v(bbMin.x, bbMin.y, bbMin.z));
		gbbVertices[1] = tMtx->transformLocation(cc3v(bbMin.x, bbMin.y, bbMax.z));
		gbbVertices[2] = tMtx->transformLocation(cc3v(bbMin.x, bbMax.y, bbMin.z));
		gbbVertices[3] = tMtx->transformLocation(cc3v(bbMin.x, bbMax.y, bbMax.z));
		gbbVertices[4] = tMtx->transformLocation(cc3v(bbMax.x, bbMin.y, bbMin.z));
		gbbVertices[5] = tMtx->transformLocation(cc3v(bbMax.x, bbMin.y, bbMax.z));
		gbbVertices[6] = tMtx->transformLocation(cc3v(bbMax.x, bbMax.y, bbMin.z));
		gbbVertices[7] = tMtx->transformLocation(cc3v(bbMax.x, bbMax.y, bbMax.z));
		
		// Construct the global mesh bounding box that surrounds the eight global vertices
		for (int i = 0; i < 8; i++) 
		{
			globalLocalContentBoundingBox = CC3BoundingBoxEngulfLocation(globalLocalContentBoundingBox, gbbVertices[i]);
		}
		
		//LogTrace(@"%@ transformed local content bounding box: %@ to global %@ using: %@",
//				 self, NSStringFromCC3BoundingBox(mbb),
//				 NSStringFromCC3BoundingBox(globalLocalContentBoundingBox), tMtx);
	}
	
	return globalLocalContentBoundingBox;
}

cocos2d::ccColor4F CC3LocalContentNode::getInitialLocalContentWireframeBoxColor()
{
	cocos2d::ccColor4F defaultColor = getLocalContentWireframeBoxColor();
	return CCC4FAreEqual(defaultColor, kCCC4FBlackTransparent)
	? cocos2d::ccc4FFromccc3B(getColor()) 
	: defaultColor;
}

void CC3LocalContentNode::checkDrawingOrder()
{
	parent->descendantDidModifySequencingCriteria(this);
	CC3Node::checkDrawingOrder();
}

void CC3LocalContentNode::setShouldDrawLocalContentWireframeBox(bool shouldDraw)
{
	// Fetch the wireframe node from the child nodes.
	CC3WireframeBoundingBoxNode* wf = getLocalContentWireframeBoxNode();
	
	// If the wireframe exists, but should not, remove it
	if (wf && !shouldDraw) 
	{
		remove();
	}
	
	// If there is no wireframe, but there should be, add it by creating a
	// CC3WireframeLocalContentBoundingBoxNode from the localContentBoundingBox
	// property and add it as a child of this node. If the bounding box is null,
	// don't create a wireframe. Since the local content of a node does not
	// normally change shape, the bounding box is NOT set to update its vertices
	// by default from the bounding box of this node on each update pass.
	if(!wf && shouldDraw) 
	{
		CC3BoundingBox mbb = getLocalContentBoundingBox();
		if ( !CC3BoundingBoxIsNull(mbb) ) 
		{
			wf = CC3WireframeLocalContentBoundingBoxNode::nodeWithName(getLocalContentWireframeBoxName());
			wf->populateAsWireBox(mbb);
			wf->setPureColor(getInitialLocalContentWireframeBoxColor());
			addChild(wf);
		}
	}
}

bool CC3LocalContentNode::getShouldDrawLocalContentWireframeBox()
{
	return (getLocalContentWireframeBoxNode() != NULL);
}

CC3WireframeBoundingBoxNode* CC3LocalContentNode::getLocalContentWireframeBoxNode()
{
	return (CC3WireframeBoundingBoxNode*)getNodeNamed(getLocalContentWireframeBoxName());
}

static cocos2d::ccColor4F localContentWireframeBoxColor = { 1.0, 0.0, 1.0, 1.0 };	// kCCC4FMagenta

cocos2d::ccColor4F CC3LocalContentNode::getLocalContentWireframeBoxColor()
{
	return localContentWireframeBoxColor;
}

void CC3LocalContentNode::setLocalContentWireframeBoxColor(cocos2d::ccColor4F aColor)
{
	localContentWireframeBoxColor = aColor;
}

bool CC3LocalContentNode::getHasLocalContent()
{
	return true;
}

cocos2d::ccColor3B CC3LocalContentNode::getInitialDescriptorColor()
{
	return CCC3BFromCCC4F(getInitialLocalContentWireframeBoxColor());
}

#define kLocalContentWireframeBoxSuffix "LCWFB"

const char* CC3LocalContentNode::getLocalContentWireframeBoxName()
{
	static char szName[256];
	sprintf(szName, "%s-%i-%s", getName().c_str(), getTag(), kLocalContentWireframeBoxSuffix);
	
	return szName;
}

bool CC3LocalContentNode::getShouldDrawAllLocalContentWireframeBoxes()
{
	if (!getShouldDrawLocalContentWireframeBox()) 
	{
		return false;
	}
	
	return CC3Node::getShouldDrawAllLocalContentWireframeBoxes();
}

void CC3LocalContentNode::setShouldDrawAllLocalContentWireframeBoxes(bool newValue)
{
	setShouldDrawLocalContentWireframeBox(newValue);
	CC3Node::setShouldDrawAllLocalContentWireframeBoxes(newValue);
}

bool CC3LocalContentNode::initWithTag(GLuint aTag, const char* aName)
{
	if ( CC3Node::initWithTag(aTag, aName) ) 
	{
		globalLocalContentBoundingBox = kCC3BoundingBoxNull;
	}
	
	return true;
}

void CC3LocalContentNode::populateFrom(CC3LocalContentNode* another)
{
	CC3Node::populateFrom(another);
	
	// Could create a child node
	setShouldDrawLocalContentWireframeBox(another->getShouldDrawLocalContentWireframeBox());
}

void CC3LocalContentNode::transformMatrixChanged()
{
	CC3Node::transformMatrixChanged();
	globalLocalContentBoundingBox = kCC3BoundingBoxNull;
}

CC3LocalContentNode* CC3LocalContentNode::node()
{
	CC3LocalContentNode* pNode = new CC3LocalContentNode;
	if (pNode) 
	{
		pNode->autorelease();
	}
	
	return pNode;
}

CC3LocalContentNode* CC3LocalContentNode::nodeWithTag(GLuint aTag)
{
	CC3LocalContentNode* pNode = new CC3LocalContentNode;
	if (pNode) 
	{
		pNode->initWithTag(aTag, pNode->getName().c_str());
		pNode->autorelease();
	}
	
	return pNode;
}

CC3LocalContentNode* CC3LocalContentNode::nodeWithName(const char* aName)
{
	CC3LocalContentNode* pNode = new CC3LocalContentNode;
	if (pNode) 
	{
		pNode->initWithName(aName);
		pNode->autorelease();
	}
	
	return pNode;
}

CC3LocalContentNode* CC3LocalContentNode::nodeWithTag(GLuint aTag, const char* aName)
{
	CC3LocalContentNode* pNode = new CC3LocalContentNode;
	if (pNode) 
	{
		pNode->initWithTag(aTag, aName);
		pNode->autorelease();
	}
	
	return pNode;
}

//@interface CC3LocalContentNode (TemplateMethods)
//@property(nonatomic, readonly) ccColor4F initialLocalContentWireframeBoxColor;
//@end
//
//@implementation CC3LocalContentNode
//
//-(BOOL) hasLocalContent {
//	return YES;
//}
//
//-(CC3BoundingBox) localContentBoundingBox {
//	return kCC3BoundingBoxNull;
//}
//
//-(CC3BoundingBox) globalLocalContentBoundingBox {
//	
//	// If the global bounding box is null, rebuild it, otherwise return it.
//	if (CC3BoundingBoxIsNull(globalLocalContentBoundingBox)) {
//		
//		// Get the mesh bounding box (in local coords). If it's null, return null.
//		CC3BoundingBox mbb = self.localContentBoundingBox;
//		if (CC3BoundingBoxIsNull(mbb)) {
//			return kCC3BoundingBoxNull;
//		}
//		
//		// The eight vertices of the transformed mesh bounding box
//		CC3Vector gbbVertices[8];
//		CC3GLMatrix* tMtx = self.transformMatrix;
//		
//		// Get the corners of the local bounding box
//		CC3Vector bbMin = mbb.minimum;
//		CC3Vector bbMax = mbb.maximum;
//		
//		// Construct all 8 corner vertices of the local bounding box and transform each
//		// to global coordinates. The result is an oriented-bounding-box.
//		gbbVertices[0] = [tMtx transformLocation: cc3v(bbMin.x, bbMin.y, bbMin.z)];
//		gbbVertices[1] = [tMtx transformLocation: cc3v(bbMin.x, bbMin.y, bbMax.z)];
//		gbbVertices[2] = [tMtx transformLocation: cc3v(bbMin.x, bbMax.y, bbMin.z)];
//		gbbVertices[3] = [tMtx transformLocation: cc3v(bbMin.x, bbMax.y, bbMax.z)];
//		gbbVertices[4] = [tMtx transformLocation: cc3v(bbMax.x, bbMin.y, bbMin.z)];
//		gbbVertices[5] = [tMtx transformLocation: cc3v(bbMax.x, bbMin.y, bbMax.z)];
//		gbbVertices[6] = [tMtx transformLocation: cc3v(bbMax.x, bbMax.y, bbMin.z)];
//		gbbVertices[7] = [tMtx transformLocation: cc3v(bbMax.x, bbMax.y, bbMax.z)];
//		
//		// Construct the global mesh bounding box that surrounds the eight global vertices
//		for (int i = 0; i < 8; i++) {
//			globalLocalContentBoundingBox = CC3BoundingBoxEngulfLocation(globalLocalContentBoundingBox, gbbVertices[i]);
//		}
//
//		LogTrace(@"%@ transformed local content bounding box: %@ to global %@ using: %@",
//				 self, NSStringFromCC3BoundingBox(mbb),
//				 NSStringFromCC3BoundingBox(globalLocalContentBoundingBox), tMtx);
//	}
//	return globalLocalContentBoundingBox;
//}
//
///** Notify up the ancestor chain...then check my children by invoking superclass implementation. */
//-(void) checkDrawingOrder {
//	[parent descendantDidModifySequencingCriteria: self];
//	[super checkDrawingOrder];
//}
//
//
//#pragma mark Allocation and initialization
//
//-(id) initWithTag: (GLuint) aTag withName: (NSString*) aName {
//	if ( (self = [super initWithTag: aTag withName: aName]) ) {
//		globalLocalContentBoundingBox = kCC3BoundingBoxNull;
//	}
//	return self;
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//// The globalLocalContentBoundingBox is left uncopied so that it will start at
//// kCC3BoundingBoxNull and be lazily created on next access.
//-(void) populateFrom: (CC3MeshNode*) another {
//	[super populateFrom: another];
//
//	// Could create a child node
//	self.shouldDrawLocalContentWireframeBox = another.shouldDrawLocalContentWireframeBox;
//}
//
//
//#pragma mark Transformations
//
///** Overridden to force a lazy recalculation of the globalLocalContentBoundingBox. */
//-(void) transformMatrixChanged {
//	[super transformMatrixChanged];
//	globalLocalContentBoundingBox = kCC3BoundingBoxNull;
//}
//
//
//#pragma mark Wireframe box and descriptor
//
///** Overridden to return local content box color */
//-(ccColor3B) initialDescriptorColor {
//	return CCC3BFromCCC4F(self.initialLocalContentWireframeBoxColor);
//}
//
///** Suffix used to name the local content wireframe. */
//#define kLocalContentWireframeBoxSuffix @"LCWFB"
//
///**
// * The name to use when creating or retrieving the wireframe node of this node's local content.
// * For uniqueness, includes the tag of this node in case this node has no name.
// */
//-(NSString*) localContentWireframeBoxName {
//	return [NSString stringWithFormat: @"%@-%u-%@", self.name, self.tag, kLocalContentWireframeBoxSuffix];
//}
//
//-(CC3WireframeBoundingBoxNode*) localContentWireframeBoxNode {
//	return (CC3WireframeBoundingBoxNode*)[self getNodeNamed: [self localContentWireframeBoxName]];
//}
//
//-(BOOL) shouldDrawLocalContentWireframeBox {
//	return (self.localContentWireframeBoxNode != nil);
//}
//
//-(void) setShouldDrawLocalContentWireframeBox: (BOOL) shouldDraw {
//	
//	// Fetch the wireframe node from the child nodes.
//	CC3WireframeBoundingBoxNode* wf = self.localContentWireframeBoxNode;
//	
//	// If the wireframe exists, but should not, remove it
//	if (wf && !shouldDraw) {
//		[wf remove];
//	}
//	
//	// If there is no wireframe, but there should be, add it by creating a
//	// CC3WireframeLocalContentBoundingBoxNode from the localContentBoundingBox
//	// property and add it as a child of this node. If the bounding box is null,
//	// don't create a wireframe. Since the local content of a node does not
//	// normally change shape, the bounding box is NOT set to update its vertices
//	// by default from the bounding box of this node on each update pass.
//	if(!wf && shouldDraw) {
//		CC3BoundingBox mbb = self.localContentBoundingBox;
//		if ( !CC3BoundingBoxIsNull(mbb) ) {
//			wf = [CC3WireframeLocalContentBoundingBoxNode nodeWithName: [self localContentWireframeBoxName]];
//			[wf populateAsWireBox: mbb];
//			wf.pureColor = self.initialLocalContentWireframeBoxColor;
//			[self addChild: wf];
//		}
//	}
//}
//
///** If default is transparent black, use the color of the node. */
//-(ccColor4F) initialLocalContentWireframeBoxColor {
//	ccColor4F defaultColor = [[self class] localContentWireframeBoxColor];
//	return CCC4FAreEqual(defaultColor, kCCC4FBlackTransparent)
//				? ccc4FFromccc3B(self.color) 
//				: defaultColor;
//}
//
//// The default color to use when drawing the wireframes of the local content
//static ccColor4F localContentWireframeBoxColor = { 1.0, 0.0, 1.0, 1.0 };	// kCCC4FMagenta
//
//+(ccColor4F) localContentWireframeBoxColor {
//	return localContentWireframeBoxColor;
//}
//
//+(void) setLocalContentWireframeBoxColor: (ccColor4F) aColor {
//	localContentWireframeBoxColor = aColor;
//}
//
//-(BOOL) shouldDrawAllLocalContentWireframeBoxes {
//	if (!self.shouldDrawLocalContentWireframeBox) {
//		return NO;
//	}
//	return super.shouldDrawAllLocalContentWireframeBoxes;
//}
//
//-(void) setShouldDrawAllLocalContentWireframeBoxes: (BOOL) shouldDraw {
//	self.shouldDrawLocalContentWireframeBox = shouldDraw;
//	super.shouldDrawAllLocalContentWireframeBoxes = shouldDraw;
//}
//
//@end


#pragma mark -
#pragma mark CC3Rotator

CC3Rotator::CC3Rotator() :
	rotationMatrix(NULL)
{
	
}

bool CC3Rotator::initOnRotationMatrix(CC3GLMatrix* aGLMatrix)
{
	
	setRotationMatrix(aGLMatrix);
	rotation = kCC3VectorZero;
	quaternion = kCC3Vector4QuaternionIdentity;
	rotationAxis = kCC3VectorZero;
	rotationAngle = 0.0;
	isRotationDirty = false;
	isQuaternionDirty = false;
	isAxisAngleDirty = false;
	isQuaternionDirtyByAxisAngle = false;
	matrixIsDirtyBy = kCC3MatrixIsNotDirty;
	
	return true;
}

CC3Rotator::~CC3Rotator()
{
	if (rotationMatrix) 
	{
		rotationMatrix->release();
	}
}

CC3GLMatrix* CC3Rotator::getRotationMatrix()
{
	applyRotation();
	
	return rotationMatrix;
}

void CC3Rotator::setRotationMatrix(CC3GLMatrix* aGLMatrix)
{
	CC3GLMatrix* oldMtx = rotationMatrix;
	rotationMatrix = aGLMatrix;
	rotationMatrix->retain();
	if (oldMtx) 
	{
		oldMtx->release();
	}
	
	isRotationDirty = true;
	isQuaternionDirty = true;
	isAxisAngleDirty = true;
	isQuaternionDirtyByAxisAngle = false;
	
	matrixIsDirtyBy = kCC3MatrixIsNotDirty;
}

void CC3Rotator::applyRotation()
{
	switch (matrixIsDirtyBy) 
	{
		case kCC3MatrixIsDirtyByRotation:
			rotationMatrix->populateFromRotation(getRotation());
			matrixIsDirtyBy = kCC3MatrixIsNotDirty;
			break;
		case kCC3MatrixIsDirtyByQuaternion:
		case kCC3MatrixIsDirtyByAxisAngle:
			rotationMatrix->populateFromQuaternion(getQuaternion());
			matrixIsDirtyBy = kCC3MatrixIsNotDirty;
			break;
		default:
			break;
	}
}

CC3Vector CC3Rotator::getRotation()
{
	ensureRotationFromMatrix();
	
	return rotation;
}

void CC3Rotator::setRotation(CC3Vector aRotation)
{
	rotation = CC3VectorRotationModulo(aRotation);
	
	isRotationDirty = false;
	isAxisAngleDirty = true;
	isQuaternionDirty = true;
	isQuaternionDirtyByAxisAngle = false;
	
	matrixIsDirtyBy = kCC3MatrixIsDirtyByRotation;
}

void CC3Rotator::ensureRotationFromMatrix()
{
	if (isRotationDirty) 
	{
		rotation = getRotationMatrix()->extractRotation();
		isRotationDirty = false;
	}
}

CC3Vector4 CC3Rotator::getQuaternion()
{
	ensureQuaternionFromAxisAngle();
	ensureQuaternionFromMatrix();
	
	return quaternion;
}

void CC3Rotator::setQuaternion(CC3Vector4 aQuaternion)
{
	quaternion = aQuaternion;
	
	isRotationDirty = true;
	isAxisAngleDirty = true;
	isQuaternionDirty = false;
	isQuaternionDirtyByAxisAngle = false;
	
	matrixIsDirtyBy = kCC3MatrixIsDirtyByQuaternion;
}

void CC3Rotator::ensureQuaternionFromAxisAngle()
{
	// If q is a quaternion, (rx, ry, rz) is the rotation axis, and ra is
	// the rotation angle (negated for right-handed coordinate system), then:
	// q = ( sin(ra/2)*rx, sin(ra/2)*ry, sin(ra/2)*rz, cos(ra/2) )
	
	if (isQuaternionDirtyByAxisAngle) 
	{
		GLfloat halfAngle = -DegreesToRadians(rotationAngle) / 2.0;		// negate for RH system
		CC3Vector axis = CC3VectorScaleUniform(CC3VectorNormalize(rotationAxis), sinf(halfAngle));
		quaternion = CC3Vector4FromCC3Vector(axis, cosf(halfAngle));
		isQuaternionDirtyByAxisAngle = false;
	}
}

void CC3Rotator::ensureQuaternionFromMatrix()
{
	if (isQuaternionDirty) 
	{
		quaternion = getRotationMatrix()->extractQuaternion();
		isQuaternionDirty = false;
	}
}

CC3Vector CC3Rotator::getRotationAxis()
{
	ensureAxisAngleFromQuaternion();
	
	return rotationAxis;
}

void CC3Rotator::setRotationAxis(CC3Vector newValue)
{
	rotationAxis = newValue;
	
	isRotationDirty = true;
	isAxisAngleDirty = false;
	isQuaternionDirty = false;
	isQuaternionDirtyByAxisAngle = true;
	
	matrixIsDirtyBy = kCC3MatrixIsDirtyByAxisAngle;
}

void CC3Rotator::ensureAxisAngleFromQuaternion()
{
	// If q is a quaternion, (rx, ry, rz) is the rotation axis, and ra is
	// the rotation angle (negated for right-handed coordinate system), then:
	// q = ( sin(ra/2)*rx, sin(ra/2)*ry, sin(ra/2)*rz, cos(ra/2) )
	// ra = acos(q.w) * 2
	// (rx, ry, rz) = (q.x, q.y, q.z) / sin(ra/2)
	
	if (isAxisAngleDirty) 
	{
		CC3Vector4 q = CC3Vector4Normalize(getQuaternion());
		GLfloat halfAngle = acosf(q.w);
		rotationAngle = -RadiansToDegrees(halfAngle) * 2.0;		// negate for RH system
		
		// If angle is zero, rotation axis is undefined. Use zero vector.
		if (halfAngle != 0.0f) 
		{
			rotationAxis = CC3VectorScaleUniform(CC3VectorFromTruncatedCC3Vector4(q),
												 (1.0 / sinf(halfAngle)));
		} 
		else 
		{
			rotationAxis = kCC3VectorZero;
		}
		
		isAxisAngleDirty = false;
	}
}

GLfloat CC3Rotator::getRotationAngle()
{
	ensureAxisAngleFromQuaternion();
	
	return rotationAngle;
}

void CC3Rotator::setRotationAngle(GLfloat anAngle)
{
	rotationAngle = Cyclic(anAngle, kCircleDegreesPeriod);
	
	isRotationDirty = true;
	isAxisAngleDirty = false;
	isQuaternionDirty = false;
	isQuaternionDirtyByAxisAngle = true;
	
	matrixIsDirtyBy = kCC3MatrixIsDirtyByAxisAngle;
}

CC3Rotator* CC3Rotator::rotator()
{
	CC3Rotator* pRotator = new CC3Rotator;
	if (pRotator) 
	{
		pRotator->initOnRotationMatrix(CC3GLMatrix::identity());
		pRotator->autorelease();
	}
	
	return pRotator;
}

CC3Rotator* CC3Rotator::rotatorOnRotationMatrix(CC3GLMatrix* aGLMatrix)
{
	CC3Rotator* pRotator = new CC3Rotator;
	if (pRotator) 
	{
		pRotator->initOnRotationMatrix(aGLMatrix);
		pRotator->autorelease();
	}
	
	return pRotator;
}

void CC3Rotator::applyRotationTo(CC3GLMatrix* aMatrix)
{
	aMatrix->multiplyByMatrix(getRotationMatrix());	// Rotation matrix is built lazily if needed
}

void CC3Rotator::populateFrom(CC3Rotator* another)
{
	rotationMatrix->release();
	
	rotationMatrix = another->getRotationMatrix();//->copy();			// retained
	rotationMatrix->retain();
	
	rotation = another->getRotation();
	quaternion = another->getQuaternion();
	rotationAxis = another->getRotationAxis();
	rotationAngle = another->getRotationAngle();
	isRotationDirty = another->getIsRotationDirty();
	isAxisAngleDirty = another->getIsAxisAngleDirty();
	isQuaternionDirty = another->getIsQuaternionDirty();
	isQuaternionDirtyByAxisAngle = another->getIsQuaternionDirtyByAxisAngle();
	matrixIsDirtyBy = another->getMatrixIsDirtyBy();
}

bool CC3Rotator::getIsRotationDirty()
{
	return isRotationDirty;
}

bool CC3Rotator::getIsMatrixDirtyByRotation()
{
	return false;
}

bool CC3Rotator::getIsQuaternionDirty()
{
	return isQuaternionDirty;
}

bool CC3Rotator::getIsMatrixDirtyByQuaternion()
{
	return false;
}

bool CC3Rotator::getIsAxisAngleDirty()
{
	return isAxisAngleDirty;
}

bool CC3Rotator::getIsQuaternionDirtyByAxisAngle()
{
	return isQuaternionDirtyByAxisAngle;
}

//@interface CC3Rotator (TemplateMethods)
//-(void) ensureRotationFromMatrix;
//-(void) ensureQuaternionFromMatrix;
//-(void) ensureQuaternionFromAxisAngle;
//-(void) ensureAxisAngleFromQuaternion;
//-(void) applyRotation;
//-(void) populateFrom: (CC3Rotator*) another;
//@property(nonatomic, readonly) BOOL isRotationDirty;
//@property(nonatomic, readonly) BOOL isMatrixDirtyByRotation;
//@property(nonatomic, readonly) BOOL isQuaternionDirty;
//@property(nonatomic, readonly) BOOL isMatrixDirtyByQuaternion;
//@property(nonatomic, readonly) BOOL isAxisAngleDirty;
//@property(nonatomic, readonly) BOOL isQuaternionDirtyByAxisAngle;
//@end
//
//@implementation CC3Rotator
//
//-(void) dealloc {
//	[rotationMatrix release];
//	[super dealloc];
//}
//
//-(CC3Vector) rotation {
//	[self ensureRotationFromMatrix];
//	return rotation;
//}
//
//-(void) setRotation:(CC3Vector) aRotation {
//	rotation = CC3VectorRotationModulo(aRotation);
//
//	isRotationDirty = NO;
//	isAxisAngleDirty = YES;
//	isQuaternionDirty = YES;
//	isQuaternionDirtyByAxisAngle = NO;
//
//	matrixIsDirtyBy = kCC3MatrixIsDirtyByRotation;
//}
//
//-(CC3Vector4) quaternion {
//	[self ensureQuaternionFromAxisAngle];
//	[self ensureQuaternionFromMatrix];
//	return quaternion;
//}
//
//-(void) setQuaternion:(CC3Vector4) aQuaternion {
//	quaternion = aQuaternion;
//
//	isRotationDirty = YES;
//	isAxisAngleDirty = YES;
//	isQuaternionDirty = NO;
//	isQuaternionDirtyByAxisAngle = NO;
//
//	matrixIsDirtyBy = kCC3MatrixIsDirtyByQuaternion;
//}
//
//-(CC3Vector) rotationAxis {
//	[self ensureAxisAngleFromQuaternion];
//	return rotationAxis;
//}
//
//-(void) setRotationAxis: (CC3Vector) aDirection {
//	rotationAxis = aDirection;
//	
//	isRotationDirty = YES;
//	isAxisAngleDirty = NO;
//	isQuaternionDirty = NO;
//	isQuaternionDirtyByAxisAngle = YES;
//	
//	matrixIsDirtyBy = kCC3MatrixIsDirtyByAxisAngle;
//}
//
//-(GLfloat) rotationAngle {
//	[self ensureAxisAngleFromQuaternion];
//	return rotationAngle;
//}
//
//-(void) setRotationAngle: (GLfloat) anAngle {
//	rotationAngle = Cyclic(anAngle, kCircleDegreesPeriod);
//	
//	isRotationDirty = YES;
//	isAxisAngleDirty = NO;
//	isQuaternionDirty = NO;
//	isQuaternionDirtyByAxisAngle = YES;
//	
//	matrixIsDirtyBy = kCC3MatrixIsDirtyByAxisAngle;
//}
//
//-(CC3GLMatrix*) rotationMatrix {
//	[self applyRotation];
//	return rotationMatrix;
//}
//
//-(void) setRotationMatrix:(CC3GLMatrix*) aGLMatrix {
//	id oldMtx = rotationMatrix;
//	rotationMatrix = [aGLMatrix retain];
//	[oldMtx release];
//	
//	isRotationDirty = YES;
//	isQuaternionDirty = YES;
//	isAxisAngleDirty = YES;
//	isQuaternionDirtyByAxisAngle = NO;
//
//	matrixIsDirtyBy = kCC3MatrixIsNotDirty;
//}
//
//-(id) init {
//	return [self initOnRotationMatrix: [CC3GLMatrix identity]];
//}
//
//+(id) rotator {
//	return [[[self alloc] init] autorelease];
//}
//
//-(id) initOnRotationMatrix: (CC3GLMatrix*) aGLMatrix {
//	if ( (self = [super init]) ) {
//		self.rotationMatrix = aGLMatrix;
//		rotation = kCC3VectorZero;
//		quaternion = kCC3Vector4QuaternionIdentity;
//		rotationAxis = kCC3VectorZero;
//		rotationAngle = 0.0;
//		isRotationDirty = NO;
//		isQuaternionDirty = NO;
//		isAxisAngleDirty = NO;
//		isQuaternionDirtyByAxisAngle = NO;
//		matrixIsDirtyBy = kCC3MatrixIsNotDirty;
//	}
//	return self;
//}
//
//+(id) rotatorOnRotationMatrix: (CC3GLMatrix*) aGLMatrix {
//	return [[[self alloc] initOnRotationMatrix: aGLMatrix] autorelease];
//}
//
//// Protected properties for copying
//-(BOOL) isRotationDirty { return isRotationDirty; }
//-(BOOL) isAxisAngleDirty { return isAxisAngleDirty; }
//-(BOOL) isQuaternionDirty { return isQuaternionDirty; }
//-(BOOL) isQuaternionDirtyByAxisAngle { return isQuaternionDirtyByAxisAngle; }
//-(BOOL) matrixIsDirtyBy { return matrixIsDirtyBy; }
//
//-(id) copyWithZone: (NSZone*) zone {
//	CC3Rotator* aCopy = [[[self class] allocWithZone: zone] init];
//	[aCopy populateFrom: self];
//	return aCopy;
//}
//
//// Template method that populates this instance from the specified other instance.
//// This method is invoked automatically during object copying via the copyWithZone: method.
//-(void) populateFrom: (CC3Rotator*) another {
//
//	[rotationMatrix release];
//	rotationMatrix = [another.rotationMatrix copy];			// retained
//
//	rotation = another.rotation;
//	quaternion = another.quaternion;
//	rotationAxis = another.rotationAxis;
//	rotationAngle = another.rotationAngle;
//	isRotationDirty = another.isRotationDirty;
//	isAxisAngleDirty = another.isAxisAngleDirty;
//	isQuaternionDirty = another.isQuaternionDirty;
//	isQuaternionDirtyByAxisAngle = another.isQuaternionDirtyByAxisAngle;
//	matrixIsDirtyBy = another.matrixIsDirtyBy;
//}
//
///** If needed, extracts and sets the rotation Euler angles from the encapsulated rotation matrix. */
//-(void) ensureRotationFromMatrix {
//	if (isRotationDirty) {
//		rotation = [self.rotationMatrix extractRotation];
//		isRotationDirty = NO;
//	}
//}
//
///** If needed, extracts and sets the quaternion from the encapsulated rotation matrix. */
//-(void) ensureQuaternionFromMatrix {
//	if (isQuaternionDirty) {
//		quaternion = [self.rotationMatrix extractQuaternion];
//		isQuaternionDirty = NO;
//	}
//}
//
///** If needed, extracts and sets the quaternion from the encapsulated rotation axis and angle. */
//-(void) ensureQuaternionFromAxisAngle {
//	// If q is a quaternion, (rx, ry, rz) is the rotation axis, and ra is
//	// the rotation angle (negated for right-handed coordinate system), then:
//	// q = ( sin(ra/2)*rx, sin(ra/2)*ry, sin(ra/2)*rz, cos(ra/2) )
//
//	if (isQuaternionDirtyByAxisAngle) {
//		GLfloat halfAngle = -DegreesToRadians(rotationAngle) / 2.0;		// negate for RH system
//		CC3Vector axis = CC3VectorScaleUniform(CC3VectorNormalize(rotationAxis), sinf(halfAngle));
//		quaternion = CC3Vector4FromCC3Vector(axis, cosf(halfAngle));
//		isQuaternionDirtyByAxisAngle = NO;
//	}
//}
//
///**
// * If needed, extracts and returns a rotation axis and angle from the encapsulated quaternion.
// * If the rotation angle is zero, the axis is undefined, and will be set to the zero vector.
// */
//-(void) ensureAxisAngleFromQuaternion {
//	// If q is a quaternion, (rx, ry, rz) is the rotation axis, and ra is
//	// the rotation angle (negated for right-handed coordinate system), then:
//	// q = ( sin(ra/2)*rx, sin(ra/2)*ry, sin(ra/2)*rz, cos(ra/2) )
//	// ra = acos(q.w) * 2
//	// (rx, ry, rz) = (q.x, q.y, q.z) / sin(ra/2)
//
//	if (isAxisAngleDirty) {
//		CC3Vector4 q = CC3Vector4Normalize(self.quaternion);
//		GLfloat halfAngle = acosf(q.w);
//		rotationAngle = -RadiansToDegrees(halfAngle) * 2.0;		// negate for RH system
//
//		// If angle is zero, rotation axis is undefined. Use zero vector.
//		if (halfAngle != 0.0f) {
//			rotationAxis = CC3VectorScaleUniform(CC3VectorFromTruncatedCC3Vector4(q),
//												 (1.0 / sinf(halfAngle)));
//		} else {
//			rotationAxis = kCC3VectorZero;
//		}
//		isAxisAngleDirty = NO;
//	}
//}
//
///** Recalculates the rotation matrix from the most recently set rotation or quaternion property. */
//-(void) applyRotation {
//	switch (matrixIsDirtyBy) {
//		case kCC3MatrixIsDirtyByRotation:
//			[rotationMatrix populateFromRotation: self.rotation];
//			matrixIsDirtyBy = kCC3MatrixIsNotDirty;
//			break;
//		case kCC3MatrixIsDirtyByQuaternion:
//		case kCC3MatrixIsDirtyByAxisAngle:
//			[rotationMatrix populateFromQuaternion: self.quaternion];
//			matrixIsDirtyBy = kCC3MatrixIsNotDirty;
//			break;
//		default:
//			break;
//	}
//}
//
//-(void) applyRotationTo: (CC3GLMatrix*) aMatrix {
//	[aMatrix multiplyByMatrix: self.rotationMatrix];	// Rotation matrix is built lazily if needed
//}
//
//-(NSString*) description {
//	return [NSString stringWithFormat: @"%@ with rotation: %@, quaternion: %@, rotation axis: %@, rotation angle %.3f",
//			[self class],
//			NSStringFromCC3Vector(self.rotation),
//			NSStringFromCC3Vector4(self.quaternion),
//			NSStringFromCC3Vector(self.rotationAxis),
//			self.rotationAngle];
//}
//
//@end
